@temporalio/core-bridge 1.6.0 → 1.7.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.
- package/Cargo.lock +520 -456
- package/lib/index.d.ts +8 -6
- package/lib/index.js.map +1 -1
- package/package.json +8 -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/sdk-core/.buildkite/docker/Dockerfile +2 -2
- package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
- package/sdk-core/.buildkite/pipeline.yml +1 -1
- package/sdk-core/.github/workflows/heavy.yml +1 -0
- package/sdk-core/README.md +13 -7
- package/sdk-core/client/src/lib.rs +27 -9
- package/sdk-core/client/src/metrics.rs +17 -8
- package/sdk-core/client/src/raw.rs +3 -3
- package/sdk-core/core/Cargo.toml +3 -4
- package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
- package/sdk-core/core/src/abstractions.rs +197 -18
- package/sdk-core/core/src/core_tests/activity_tasks.rs +137 -45
- package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
- package/sdk-core/core/src/core_tests/determinism.rs +212 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +183 -36
- package/sdk-core/core/src/core_tests/queries.rs +32 -14
- package/sdk-core/core/src/core_tests/workers.rs +8 -5
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +340 -51
- package/sdk-core/core/src/ephemeral_server/mod.rs +110 -8
- package/sdk-core/core/src/internal_flags.rs +141 -0
- package/sdk-core/core/src/lib.rs +14 -9
- package/sdk-core/core/src/replay/mod.rs +16 -27
- package/sdk-core/core/src/telemetry/metrics.rs +69 -35
- package/sdk-core/core/src/telemetry/mod.rs +38 -14
- package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
- package/sdk-core/core/src/test_help/mod.rs +65 -13
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
- package/sdk-core/core/src/worker/activities/local_activities.rs +122 -6
- package/sdk-core/core/src/worker/activities.rs +347 -173
- package/sdk-core/core/src/worker/client/mocks.rs +22 -2
- package/sdk-core/core/src/worker/client.rs +18 -2
- package/sdk-core/core/src/worker/mod.rs +137 -44
- package/sdk-core/core/src/worker/workflow/history_update.rs +132 -51
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +207 -166
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +6 -7
- package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -7
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +157 -82
- package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +12 -12
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +6 -7
- package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +13 -15
- package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +170 -60
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +24 -16
- package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +6 -8
- package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +320 -204
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +10 -13
- package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +15 -23
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +187 -46
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +237 -111
- package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +13 -13
- package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +10 -6
- package/sdk-core/core/src/worker/workflow/managed_run.rs +81 -62
- package/sdk-core/core/src/worker/workflow/mod.rs +341 -79
- package/sdk-core/core/src/worker/workflow/run_cache.rs +18 -11
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +15 -3
- package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -0
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +75 -52
- package/sdk-core/core-api/Cargo.toml +0 -1
- package/sdk-core/core-api/src/lib.rs +13 -7
- package/sdk-core/core-api/src/telemetry.rs +4 -6
- package/sdk-core/core-api/src/worker.rs +5 -0
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +80 -55
- package/sdk-core/fsm/rustfsm_trait/src/lib.rs +22 -68
- package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
- package/sdk-core/histories/old_change_marker_format.bin +0 -0
- package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
- package/sdk-core/protos/api_upstream/Makefile +1 -1
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
- package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
- package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
- package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
- package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
- package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
- package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
- package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
- package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
- package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +65 -60
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
- package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
- package/sdk-core/sdk/Cargo.toml +1 -1
- package/sdk-core/sdk/src/lib.rs +21 -5
- package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
- package/sdk-core/sdk/src/workflow_context.rs +24 -17
- package/sdk-core/sdk/src/workflow_future.rs +9 -3
- package/sdk-core/sdk-core-protos/src/history_builder.rs +114 -89
- package/sdk-core/sdk-core-protos/src/history_info.rs +6 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +205 -64
- package/sdk-core/test-utils/src/canned_histories.rs +106 -296
- package/sdk-core/test-utils/src/lib.rs +32 -5
- package/sdk-core/tests/heavy_tests.rs +10 -43
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
- package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
- package/sdk-core/tests/integ_tests/polling_tests.rs +3 -8
- package/sdk-core/tests/integ_tests/queries_tests.rs +4 -2
- package/sdk-core/tests/integ_tests/visibility_tests.rs +34 -23
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +97 -81
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +5 -1
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +25 -3
- package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +30 -0
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +64 -0
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +4 -0
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +3 -1
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +7 -2
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -7
- package/sdk-core/tests/integ_tests/workflow_tests.rs +8 -8
- package/sdk-core/tests/main.rs +16 -25
- package/sdk-core/tests/runner.rs +11 -9
- package/src/conversions.rs +14 -8
- package/src/runtime.rs +9 -8
- package/ts/index.ts +8 -6
- package/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
use anyhow::anyhow;
|
|
2
2
|
use assert_matches::assert_matches;
|
|
3
|
+
use futures_util::future::join_all;
|
|
3
4
|
use std::time::Duration;
|
|
4
5
|
use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowExecutionResult, WorkflowOptions};
|
|
5
6
|
use temporal_sdk::{
|
|
6
|
-
ActContext, ActExitValue, ActivityOptions, CancellableFuture,
|
|
7
|
+
ActContext, ActExitValue, ActivityCancelledError, ActivityOptions, CancellableFuture,
|
|
8
|
+
WfContext, WorkflowResult,
|
|
7
9
|
};
|
|
8
10
|
use temporal_sdk_core_protos::{
|
|
9
11
|
coresdk::{
|
|
@@ -20,16 +22,17 @@ use temporal_sdk_core_protos::{
|
|
|
20
22
|
IntoCompletion,
|
|
21
23
|
},
|
|
22
24
|
temporal::api::{
|
|
23
|
-
common::v1::{ActivityType, Payload, Payloads},
|
|
25
|
+
common::v1::{ActivityType, Payload, Payloads, RetryPolicy},
|
|
24
26
|
enums::v1::RetryState,
|
|
25
27
|
failure::v1::{failure::FailureInfo, ActivityFailureInfo, Failure},
|
|
26
28
|
},
|
|
27
|
-
TaskToken,
|
|
29
|
+
TaskToken, DEFAULT_ACTIVITY_TYPE,
|
|
28
30
|
};
|
|
29
31
|
use temporal_sdk_core_test_utils::{
|
|
30
|
-
init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter,
|
|
32
|
+
drain_pollers_and_shutdown, init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter,
|
|
33
|
+
WorkerTestHelpers,
|
|
31
34
|
};
|
|
32
|
-
use tokio::time::sleep;
|
|
35
|
+
use tokio::{join, sync::Semaphore, time::sleep};
|
|
33
36
|
|
|
34
37
|
pub async fn one_activity_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
35
38
|
ctx.activity(ActivityOptions {
|
|
@@ -98,7 +101,7 @@ async fn activity_workflow() {
|
|
|
98
101
|
assert_matches!(
|
|
99
102
|
task.variant,
|
|
100
103
|
Some(act_task::Variant::Start(start_activity)) => {
|
|
101
|
-
assert_eq!(start_activity.activity_type,
|
|
104
|
+
assert_eq!(start_activity.activity_type, DEFAULT_ACTIVITY_TYPE.to_string())
|
|
102
105
|
}
|
|
103
106
|
);
|
|
104
107
|
let response_payload = Payload {
|
|
@@ -155,14 +158,9 @@ async fn activity_non_retryable_failure() {
|
|
|
155
158
|
)
|
|
156
159
|
.await
|
|
157
160
|
.unwrap();
|
|
158
|
-
// Poll activity and verify that it's been scheduled
|
|
161
|
+
// Poll activity and verify that it's been scheduled
|
|
159
162
|
let task = core.poll_activity_task().await.unwrap();
|
|
160
|
-
assert_matches!(
|
|
161
|
-
task.variant,
|
|
162
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
163
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
164
|
-
}
|
|
165
|
-
);
|
|
163
|
+
assert_matches!(task.variant, Some(act_task::Variant::Start(_)));
|
|
166
164
|
// Fail activity with non-retryable error
|
|
167
165
|
let failure = Failure::application_failure("activity failed".to_string(), true);
|
|
168
166
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
@@ -192,7 +190,7 @@ async fn activity_non_retryable_failure() {
|
|
|
192
190
|
failure_info: Some(FailureInfo::ActivityFailureInfo(ActivityFailureInfo{
|
|
193
191
|
activity_id: "act-1".to_owned(),
|
|
194
192
|
activity_type: Some(ActivityType {
|
|
195
|
-
name:
|
|
193
|
+
name: DEFAULT_ACTIVITY_TYPE.to_owned(),
|
|
196
194
|
}),
|
|
197
195
|
scheduled_event_id: 5,
|
|
198
196
|
started_event_id: 6,
|
|
@@ -227,14 +225,9 @@ async fn activity_non_retryable_failure_with_error() {
|
|
|
227
225
|
)
|
|
228
226
|
.await
|
|
229
227
|
.unwrap();
|
|
230
|
-
// Poll activity and verify that it's been scheduled
|
|
228
|
+
// Poll activity and verify that it's been scheduled
|
|
231
229
|
let task = core.poll_activity_task().await.unwrap();
|
|
232
|
-
assert_matches!(
|
|
233
|
-
task.variant,
|
|
234
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
235
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
236
|
-
}
|
|
237
|
-
);
|
|
230
|
+
assert_matches!(task.variant, Some(act_task::Variant::Start(_)));
|
|
238
231
|
// Fail activity with non-retryable error
|
|
239
232
|
let failure = Failure::application_failure_from_error(anyhow!("activity failed"), true);
|
|
240
233
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
@@ -264,7 +257,7 @@ async fn activity_non_retryable_failure_with_error() {
|
|
|
264
257
|
failure_info: Some(FailureInfo::ActivityFailureInfo(ActivityFailureInfo{
|
|
265
258
|
activity_id: "act-1".to_owned(),
|
|
266
259
|
activity_type: Some(ActivityType {
|
|
267
|
-
name:
|
|
260
|
+
name: DEFAULT_ACTIVITY_TYPE.to_owned(),
|
|
268
261
|
}),
|
|
269
262
|
scheduled_event_id: 5,
|
|
270
263
|
started_event_id: 6,
|
|
@@ -301,12 +294,7 @@ async fn activity_retry() {
|
|
|
301
294
|
.unwrap();
|
|
302
295
|
// Poll activity 1st time
|
|
303
296
|
let task = core.poll_activity_task().await.unwrap();
|
|
304
|
-
assert_matches!(
|
|
305
|
-
task.variant,
|
|
306
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
307
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
308
|
-
}
|
|
309
|
-
);
|
|
297
|
+
assert_matches!(task.variant, Some(act_task::Variant::Start(_)));
|
|
310
298
|
// Fail activity with retryable error
|
|
311
299
|
let failure = Failure::application_failure("activity failed".to_string(), false);
|
|
312
300
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
@@ -317,12 +305,7 @@ async fn activity_retry() {
|
|
|
317
305
|
.unwrap();
|
|
318
306
|
// Poll 2nd time
|
|
319
307
|
let task = core.poll_activity_task().await.unwrap();
|
|
320
|
-
assert_matches!(
|
|
321
|
-
task.variant,
|
|
322
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
323
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
324
|
-
}
|
|
325
|
-
);
|
|
308
|
+
assert_matches!(task.variant, Some(act_task::Variant::Start(_)));
|
|
326
309
|
// Complete activity successfully
|
|
327
310
|
let response_payload = Payload {
|
|
328
311
|
data: b"hello ".to_vec(),
|
|
@@ -381,15 +364,10 @@ async fn activity_cancellation_try_cancel() {
|
|
|
381
364
|
)
|
|
382
365
|
.await
|
|
383
366
|
.unwrap();
|
|
384
|
-
// Poll activity and verify that it's been scheduled
|
|
385
|
-
//
|
|
367
|
+
// Poll activity and verify that it's been scheduled, we don't expect to complete it in this
|
|
368
|
+
// test as activity is try-cancelled.
|
|
386
369
|
let activity_task = core.poll_activity_task().await.unwrap();
|
|
387
|
-
assert_matches!(
|
|
388
|
-
activity_task.variant,
|
|
389
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
390
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
391
|
-
}
|
|
392
|
-
);
|
|
370
|
+
assert_matches!(activity_task.variant, Some(act_task::Variant::Start(_)));
|
|
393
371
|
// Poll workflow task and verify that activity has failed.
|
|
394
372
|
let task = core.poll_workflow_activation().await.unwrap();
|
|
395
373
|
assert_matches!(
|
|
@@ -526,15 +504,10 @@ async fn started_activity_timeout() {
|
|
|
526
504
|
)
|
|
527
505
|
.await
|
|
528
506
|
.unwrap();
|
|
529
|
-
// Poll activity and verify that it's been scheduled
|
|
530
|
-
//
|
|
507
|
+
// Poll activity and verify that it's been scheduled, we don't expect to complete it in this
|
|
508
|
+
// test as activity is timed out after 1 second.
|
|
531
509
|
let activity_task = core.poll_activity_task().await.unwrap();
|
|
532
|
-
assert_matches!(
|
|
533
|
-
activity_task.variant,
|
|
534
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
535
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
536
|
-
}
|
|
537
|
-
);
|
|
510
|
+
assert_matches!(activity_task.variant, Some(act_task::Variant::Start(_)));
|
|
538
511
|
let task = core.poll_workflow_activation().await.unwrap();
|
|
539
512
|
assert_matches!(
|
|
540
513
|
task.jobs.as_slice(),
|
|
@@ -590,15 +563,10 @@ async fn activity_cancellation_wait_cancellation_completed() {
|
|
|
590
563
|
)
|
|
591
564
|
.await
|
|
592
565
|
.unwrap();
|
|
593
|
-
// Poll activity and verify that it's been scheduled
|
|
594
|
-
//
|
|
566
|
+
// Poll activity and verify that it's been scheduled, we don't expect to complete it in this
|
|
567
|
+
// test as activity is wait-cancelled.
|
|
595
568
|
let activity_task = core.poll_activity_task().await.unwrap();
|
|
596
|
-
assert_matches!(
|
|
597
|
-
activity_task.variant,
|
|
598
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
599
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
600
|
-
}
|
|
601
|
-
);
|
|
569
|
+
assert_matches!(activity_task.variant, Some(act_task::Variant::Start(_)));
|
|
602
570
|
// Poll workflow task and verify that activity has failed.
|
|
603
571
|
let task = core.poll_workflow_activation().await.unwrap();
|
|
604
572
|
assert_matches!(
|
|
@@ -657,15 +625,10 @@ async fn activity_cancellation_abandon() {
|
|
|
657
625
|
)
|
|
658
626
|
.await
|
|
659
627
|
.unwrap();
|
|
660
|
-
// Poll activity and verify that it's been scheduled
|
|
661
|
-
//
|
|
628
|
+
// Poll activity and verify that it's been scheduled, we don't expect to complete it in this
|
|
629
|
+
// test as activity is abandoned.
|
|
662
630
|
let activity_task = core.poll_activity_task().await.unwrap();
|
|
663
|
-
assert_matches!(
|
|
664
|
-
activity_task.variant,
|
|
665
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
666
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
667
|
-
}
|
|
668
|
-
);
|
|
631
|
+
assert_matches!(activity_task.variant, Some(act_task::Variant::Start(_)));
|
|
669
632
|
// Poll workflow task and verify that activity has failed.
|
|
670
633
|
let task = core.poll_workflow_activation().await.unwrap();
|
|
671
634
|
assert_matches!(
|
|
@@ -713,14 +676,9 @@ async fn async_activity_completion_workflow() {
|
|
|
713
676
|
)
|
|
714
677
|
.await
|
|
715
678
|
.unwrap();
|
|
716
|
-
// Poll activity and verify that it's been scheduled
|
|
679
|
+
// Poll activity and verify that it's been scheduled
|
|
717
680
|
let task = core.poll_activity_task().await.unwrap();
|
|
718
|
-
assert_matches!(
|
|
719
|
-
task.variant,
|
|
720
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
721
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
722
|
-
}
|
|
723
|
-
);
|
|
681
|
+
assert_matches!(task.variant, Some(act_task::Variant::Start(_)));
|
|
724
682
|
let response_payload = Payload {
|
|
725
683
|
data: b"hello ".to_vec(),
|
|
726
684
|
metadata: Default::default(),
|
|
@@ -785,14 +743,9 @@ async fn activity_cancelled_after_heartbeat_times_out() {
|
|
|
785
743
|
)
|
|
786
744
|
.await
|
|
787
745
|
.unwrap();
|
|
788
|
-
// Poll activity and verify that it's been scheduled
|
|
746
|
+
// Poll activity and verify that it's been scheduled
|
|
789
747
|
let task = core.poll_activity_task().await.unwrap();
|
|
790
|
-
assert_matches!(
|
|
791
|
-
task.variant,
|
|
792
|
-
Some(act_task::Variant::Start(start_activity)) => {
|
|
793
|
-
assert_eq!(start_activity.activity_type, "test_activity".to_string())
|
|
794
|
-
}
|
|
795
|
-
);
|
|
748
|
+
assert_matches!(task.variant, Some(act_task::Variant::Start(_)));
|
|
796
749
|
// Delay the heartbeat
|
|
797
750
|
sleep(Duration::from_secs(2)).await;
|
|
798
751
|
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
@@ -814,7 +767,7 @@ async fn activity_cancelled_after_heartbeat_times_out() {
|
|
|
814
767
|
.unwrap();
|
|
815
768
|
|
|
816
769
|
// Verify shutdown completes
|
|
817
|
-
core
|
|
770
|
+
drain_pollers_and_shutdown(&core).await;
|
|
818
771
|
// Cleanup just in case
|
|
819
772
|
starter
|
|
820
773
|
.get_client()
|
|
@@ -949,3 +902,66 @@ async fn it_can_complete_async() {
|
|
|
949
902
|
|
|
950
903
|
worker.run_until_done().await.unwrap();
|
|
951
904
|
}
|
|
905
|
+
|
|
906
|
+
#[tokio::test]
|
|
907
|
+
async fn graceful_shutdown() {
|
|
908
|
+
let wf_name = "graceful_shutdown";
|
|
909
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
910
|
+
starter.worker_config.graceful_shutdown_period = Some(Duration::from_millis(500));
|
|
911
|
+
let mut worker = starter.worker().await;
|
|
912
|
+
let client = starter.get_client().await;
|
|
913
|
+
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
914
|
+
let act_futs = (1..=10).map(|_| {
|
|
915
|
+
ctx.activity(ActivityOptions {
|
|
916
|
+
activity_type: "sleeper".to_string(),
|
|
917
|
+
start_to_close_timeout: Some(Duration::from_secs(5)),
|
|
918
|
+
retry_policy: Some(RetryPolicy {
|
|
919
|
+
maximum_attempts: 1,
|
|
920
|
+
..Default::default()
|
|
921
|
+
}),
|
|
922
|
+
cancellation_type: ActivityCancellationType::WaitCancellationCompleted,
|
|
923
|
+
input: "hi".as_json_payload().unwrap(),
|
|
924
|
+
..Default::default()
|
|
925
|
+
})
|
|
926
|
+
});
|
|
927
|
+
join_all(act_futs).await;
|
|
928
|
+
Ok(().into())
|
|
929
|
+
});
|
|
930
|
+
static ACTS_STARTED: Semaphore = Semaphore::const_new(0);
|
|
931
|
+
static ACTS_DONE: Semaphore = Semaphore::const_new(0);
|
|
932
|
+
worker.register_activity("sleeper", |ctx: ActContext, _: String| async move {
|
|
933
|
+
ACTS_STARTED.add_permits(1);
|
|
934
|
+
// just wait to be cancelled
|
|
935
|
+
ctx.cancelled().await;
|
|
936
|
+
ACTS_DONE.add_permits(1);
|
|
937
|
+
Result::<(), _>::Err(ActivityCancelledError::default().into())
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
worker
|
|
941
|
+
.submit_wf(
|
|
942
|
+
wf_name.to_owned(),
|
|
943
|
+
wf_name.to_owned(),
|
|
944
|
+
vec![],
|
|
945
|
+
WorkflowOptions::default(),
|
|
946
|
+
)
|
|
947
|
+
.await
|
|
948
|
+
.unwrap();
|
|
949
|
+
|
|
950
|
+
let handle = worker.inner_mut().shutdown_handle();
|
|
951
|
+
let shutdowner = async {
|
|
952
|
+
// Wait for all acts to be started before initiating shutdown
|
|
953
|
+
let _ = ACTS_STARTED.acquire_many(10).await;
|
|
954
|
+
handle();
|
|
955
|
+
// Kill workflow once all acts are cancelled. This also ensures we actually see all the
|
|
956
|
+
// cancels, otherwise run_until_done will hang since the workflow won't complete.
|
|
957
|
+
let _ = ACTS_DONE.acquire_many(10).await;
|
|
958
|
+
client
|
|
959
|
+
.terminate_workflow_execution(wf_name.to_owned(), None)
|
|
960
|
+
.await
|
|
961
|
+
.unwrap();
|
|
962
|
+
};
|
|
963
|
+
let runner = async {
|
|
964
|
+
worker.run_until_done().await.unwrap();
|
|
965
|
+
};
|
|
966
|
+
join!(shutdowner, runner);
|
|
967
|
+
}
|
|
@@ -33,6 +33,7 @@ async fn cancel_receiver(mut ctx: WfContext) -> WorkflowResult<()> {
|
|
|
33
33
|
#[tokio::test]
|
|
34
34
|
async fn sends_cancel_to_other_wf() {
|
|
35
35
|
let mut starter = CoreWfStarter::new("sends_cancel_to_other_wf");
|
|
36
|
+
starter.no_remote_activities();
|
|
36
37
|
let mut worker = starter.worker().await;
|
|
37
38
|
worker.register_wf("sender", cancel_sender);
|
|
38
39
|
worker.register_wf("receiver", cancel_receiver);
|
|
@@ -21,6 +21,7 @@ async fn cancelled_wf(mut ctx: WfContext) -> WorkflowResult<()> {
|
|
|
21
21
|
async fn cancel_during_timer() {
|
|
22
22
|
let wf_name = "cancel_during_timer";
|
|
23
23
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
24
|
+
starter.no_remote_activities();
|
|
24
25
|
let mut worker = starter.worker().await;
|
|
25
26
|
let client = starter.get_client().await;
|
|
26
27
|
worker.register_wf(wf_name.to_string(), cancelled_wf);
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
use anyhow::anyhow;
|
|
2
|
-
use
|
|
3
|
-
use
|
|
4
|
-
use
|
|
2
|
+
use std::time::Duration;
|
|
3
|
+
use temporal_client::{WorkflowClientTrait, WorkflowOptions};
|
|
4
|
+
use temporal_sdk::{ChildWorkflowOptions, WfContext, WfExitValue, WorkflowResult};
|
|
5
|
+
use temporal_sdk_core_protos::{
|
|
6
|
+
coresdk::child_workflow::{child_workflow_result, ChildWorkflowCancellationType, Success},
|
|
7
|
+
temporal::api::enums::v1::ParentClosePolicy,
|
|
8
|
+
};
|
|
5
9
|
use temporal_sdk_core_test_utils::CoreWfStarter;
|
|
10
|
+
use tokio::sync::Barrier;
|
|
6
11
|
|
|
7
12
|
static PARENT_WF_TYPE: &str = "parent_wf";
|
|
8
13
|
static CHILD_WF_TYPE: &str = "child_wf";
|
|
@@ -32,6 +37,7 @@ async fn parent_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
32
37
|
#[tokio::test]
|
|
33
38
|
async fn child_workflow_happy_path() {
|
|
34
39
|
let mut starter = CoreWfStarter::new("child-workflows");
|
|
40
|
+
starter.no_remote_activities();
|
|
35
41
|
let mut worker = starter.worker().await;
|
|
36
42
|
|
|
37
43
|
worker.register_wf(PARENT_WF_TYPE.to_string(), parent_wf);
|
|
@@ -48,3 +54,74 @@ async fn child_workflow_happy_path() {
|
|
|
48
54
|
.unwrap();
|
|
49
55
|
worker.run_until_done().await.unwrap();
|
|
50
56
|
}
|
|
57
|
+
|
|
58
|
+
#[tokio::test]
|
|
59
|
+
async fn abandoned_child_bug_repro() {
|
|
60
|
+
let mut starter = CoreWfStarter::new("child-workflow-abandon-bug");
|
|
61
|
+
starter.no_remote_activities();
|
|
62
|
+
let mut worker = starter.worker().await;
|
|
63
|
+
let barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
|
|
64
|
+
|
|
65
|
+
worker.register_wf(
|
|
66
|
+
PARENT_WF_TYPE.to_string(),
|
|
67
|
+
move |mut ctx: WfContext| async move {
|
|
68
|
+
let child = ctx.child_workflow(ChildWorkflowOptions {
|
|
69
|
+
workflow_id: "abandoned-child".to_owned(),
|
|
70
|
+
workflow_type: CHILD_WF_TYPE.to_owned(),
|
|
71
|
+
parent_close_policy: ParentClosePolicy::Abandon,
|
|
72
|
+
cancel_type: ChildWorkflowCancellationType::Abandon,
|
|
73
|
+
..Default::default()
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
let started = child
|
|
77
|
+
.start(&ctx)
|
|
78
|
+
.await
|
|
79
|
+
.into_started()
|
|
80
|
+
.expect("Child chould start OK");
|
|
81
|
+
barr.wait().await;
|
|
82
|
+
// Wait for cancel signal
|
|
83
|
+
ctx.cancelled().await;
|
|
84
|
+
// Cancel the child immediately
|
|
85
|
+
started.cancel(&ctx);
|
|
86
|
+
// Need to do something else, so we'll see the ChildWorkflowExecutionCanceled event
|
|
87
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
88
|
+
started.result().await;
|
|
89
|
+
Ok(().into())
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
worker.register_wf(CHILD_WF_TYPE.to_string(), |mut ctx: WfContext| async move {
|
|
93
|
+
ctx.cancelled().await;
|
|
94
|
+
Ok(WfExitValue::Cancelled)
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
worker
|
|
98
|
+
.submit_wf(
|
|
99
|
+
"parent-abandoner".to_string(),
|
|
100
|
+
PARENT_WF_TYPE.to_owned(),
|
|
101
|
+
vec![],
|
|
102
|
+
WorkflowOptions::default(),
|
|
103
|
+
)
|
|
104
|
+
.await
|
|
105
|
+
.unwrap();
|
|
106
|
+
let client = starter.get_client().await;
|
|
107
|
+
let canceller = async {
|
|
108
|
+
barr.wait().await;
|
|
109
|
+
client
|
|
110
|
+
.cancel_workflow_execution(
|
|
111
|
+
"parent-abandoner".to_string(),
|
|
112
|
+
None,
|
|
113
|
+
"die".to_string(),
|
|
114
|
+
None,
|
|
115
|
+
)
|
|
116
|
+
.await
|
|
117
|
+
.unwrap();
|
|
118
|
+
client
|
|
119
|
+
.cancel_workflow_execution("abandoned-child".to_string(), None, "die".to_string(), None)
|
|
120
|
+
.await
|
|
121
|
+
.unwrap();
|
|
122
|
+
};
|
|
123
|
+
let runner = async move {
|
|
124
|
+
worker.run_until_done().await.unwrap();
|
|
125
|
+
};
|
|
126
|
+
tokio::join!(canceller, runner);
|
|
127
|
+
}
|
|
@@ -21,6 +21,7 @@ async fn continue_as_new_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
21
21
|
async fn continue_as_new_happy_path() {
|
|
22
22
|
let wf_name = "continue_as_new_happy_path";
|
|
23
23
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
24
|
+
starter.no_remote_activities();
|
|
24
25
|
let mut worker = starter.worker().await;
|
|
25
26
|
worker.register_wf(wf_name.to_string(), continue_as_new_wf);
|
|
26
27
|
|
|
@@ -40,7 +41,10 @@ async fn continue_as_new_happy_path() {
|
|
|
40
41
|
async fn continue_as_new_multiple_concurrent() {
|
|
41
42
|
let wf_name = "continue_as_new_multiple_concurrent";
|
|
42
43
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
43
|
-
starter
|
|
44
|
+
starter
|
|
45
|
+
.no_remote_activities()
|
|
46
|
+
.max_cached_workflows(3)
|
|
47
|
+
.max_wft(3);
|
|
44
48
|
let mut worker = starter.worker().await;
|
|
45
49
|
worker.register_wf(wf_name.to_string(), continue_as_new_wf);
|
|
46
50
|
|
|
@@ -36,6 +36,7 @@ pub async fn timer_wf_nondeterministic(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
36
36
|
async fn test_determinism_error_then_recovers() {
|
|
37
37
|
let wf_name = "test_determinism_error_then_recovers";
|
|
38
38
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
39
|
+
starter.no_remote_activities();
|
|
39
40
|
let mut worker = starter.worker().await;
|
|
40
41
|
|
|
41
42
|
worker.register_wf(wf_name.to_owned(), timer_wf_nondeterministic);
|
|
@@ -12,7 +12,8 @@ use temporal_sdk::{
|
|
|
12
12
|
use temporal_sdk_core::replay::HistoryForReplay;
|
|
13
13
|
use temporal_sdk_core_protos::{
|
|
14
14
|
coresdk::{
|
|
15
|
-
workflow_commands::ActivityCancellationType,
|
|
15
|
+
workflow_commands::workflow_command::Variant, workflow_commands::ActivityCancellationType,
|
|
16
|
+
workflow_completion, workflow_completion::workflow_activation_completion,
|
|
16
17
|
workflow_completion::WorkflowActivationCompletion, AsJsonPayloadExt,
|
|
17
18
|
},
|
|
18
19
|
temporal::api::{common::v1::RetryPolicy, enums::v1::TimeoutType},
|
|
@@ -234,6 +235,7 @@ async fn cancel_immediate(#[case] cancel_type: ActivityCancellationType) {
|
|
|
234
235
|
starter.start_with_worker(wf_name, &mut worker).await;
|
|
235
236
|
worker
|
|
236
237
|
.run_until_done_intercepted(Some(LACancellerInterceptor {
|
|
238
|
+
cancel_on_workflow_completed: false,
|
|
237
239
|
token: manual_cancel,
|
|
238
240
|
}))
|
|
239
241
|
.await
|
|
@@ -242,12 +244,29 @@ async fn cancel_immediate(#[case] cancel_type: ActivityCancellationType) {
|
|
|
242
244
|
|
|
243
245
|
struct LACancellerInterceptor {
|
|
244
246
|
token: CancellationToken,
|
|
247
|
+
cancel_on_workflow_completed: bool,
|
|
245
248
|
}
|
|
246
249
|
#[async_trait::async_trait(?Send)]
|
|
247
250
|
impl WorkerInterceptor for LACancellerInterceptor {
|
|
248
|
-
async fn on_workflow_activation_completion(&self,
|
|
251
|
+
async fn on_workflow_activation_completion(&self, completion: &WorkflowActivationCompletion) {
|
|
252
|
+
if !self.cancel_on_workflow_completed {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if let Some(workflow_activation_completion::Status::Successful(
|
|
256
|
+
workflow_completion::Success { commands, .. },
|
|
257
|
+
)) = completion.status.as_ref()
|
|
258
|
+
{
|
|
259
|
+
if let Some(&Variant::CompleteWorkflowExecution(_)) =
|
|
260
|
+
commands.last().and_then(|v| v.variant.as_ref())
|
|
261
|
+
{
|
|
262
|
+
self.token.cancel();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
249
266
|
fn on_shutdown(&self, _: &temporal_sdk::Worker) {
|
|
250
|
-
self.
|
|
267
|
+
if !self.cancel_on_workflow_completed {
|
|
268
|
+
self.token.cancel()
|
|
269
|
+
}
|
|
251
270
|
}
|
|
252
271
|
}
|
|
253
272
|
|
|
@@ -331,6 +350,9 @@ async fn cancel_after_act_starts(
|
|
|
331
350
|
worker
|
|
332
351
|
.run_until_done_intercepted(Some(LACancellerInterceptor {
|
|
333
352
|
token: manual_cancel,
|
|
353
|
+
// Only needed for this one case since the activity is not drained and prevents worker from shutting down.
|
|
354
|
+
cancel_on_workflow_completed: matches!(cancel_type, ActivityCancellationType::Abandon)
|
|
355
|
+
&& cancel_on_backoff.is_none(),
|
|
334
356
|
}))
|
|
335
357
|
.await
|
|
336
358
|
.unwrap();
|
|
@@ -20,6 +20,7 @@ async fn sends_modify_wf_props() {
|
|
|
20
20
|
let wf_name = "can_upsert_memo";
|
|
21
21
|
let wf_id = Uuid::new_v4();
|
|
22
22
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
23
|
+
starter.no_remote_activities();
|
|
23
24
|
let mut worker = starter.worker().await;
|
|
24
25
|
|
|
25
26
|
worker.register_wf(wf_name, memo_upserter);
|
|
@@ -43,10 +44,7 @@ async fn sends_modify_wf_props() {
|
|
|
43
44
|
let catname = memo.get(FIELD_A).unwrap();
|
|
44
45
|
let cuteness = memo.get(FIELD_B).unwrap();
|
|
45
46
|
for payload in [catname, cuteness] {
|
|
46
|
-
|
|
47
|
-
&b"json/plain".to_vec(),
|
|
48
|
-
payload.metadata.get("encoding").unwrap()
|
|
49
|
-
);
|
|
47
|
+
assert!(payload.is_json_payload());
|
|
50
48
|
}
|
|
51
49
|
assert_eq!("enchi", String::from_json_payload(catname).unwrap());
|
|
52
50
|
assert_eq!(9001, usize::from_json_payload(cuteness).unwrap());
|
|
@@ -27,6 +27,7 @@ pub async fn changes_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
27
27
|
async fn writes_change_markers() {
|
|
28
28
|
let wf_name = "writes_change_markers";
|
|
29
29
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
30
|
+
starter.no_remote_activities();
|
|
30
31
|
let mut worker = starter.worker().await;
|
|
31
32
|
worker.register_wf(wf_name.to_owned(), changes_wf);
|
|
32
33
|
|
|
@@ -59,6 +60,7 @@ pub async fn no_change_then_change_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
59
60
|
async fn can_add_change_markers() {
|
|
60
61
|
let wf_name = "can_add_change_markers";
|
|
61
62
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
63
|
+
starter.no_remote_activities();
|
|
62
64
|
let mut worker = starter.worker().await;
|
|
63
65
|
worker.register_wf(wf_name.to_owned(), no_change_then_change_wf);
|
|
64
66
|
|
|
@@ -81,9 +83,37 @@ pub async fn replay_with_change_marker_wf(ctx: WfContext) -> WorkflowResult<()>
|
|
|
81
83
|
async fn replaying_with_patch_marker() {
|
|
82
84
|
let wf_name = "replaying_with_patch_marker";
|
|
83
85
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
86
|
+
starter.no_remote_activities();
|
|
84
87
|
let mut worker = starter.worker().await;
|
|
85
88
|
worker.register_wf(wf_name.to_owned(), replay_with_change_marker_wf);
|
|
86
89
|
|
|
87
90
|
starter.start_with_worker(wf_name, &mut worker).await;
|
|
88
91
|
worker.run_until_done().await.unwrap();
|
|
89
92
|
}
|
|
93
|
+
|
|
94
|
+
/// Test that the internal patching mechanism works on the second workflow task when replaying.
|
|
95
|
+
/// Used as regression test for a bug that detected that we did not look ahead far enough to find
|
|
96
|
+
/// the next workflow task completion, which the flags are attached to.
|
|
97
|
+
#[tokio::test]
|
|
98
|
+
async fn patched_on_second_workflow_task_is_deterministic() {
|
|
99
|
+
let wf_name = "timer_patched_timer";
|
|
100
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
101
|
+
// Disable caching to force replay from beginning
|
|
102
|
+
starter.max_cached_workflows(0).no_remote_activities();
|
|
103
|
+
let mut worker = starter.worker().await;
|
|
104
|
+
// Include a task failure as well to make sure that works
|
|
105
|
+
static FAIL_ONCE: AtomicBool = AtomicBool::new(true);
|
|
106
|
+
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
107
|
+
ctx.timer(Duration::from_millis(1)).await;
|
|
108
|
+
if FAIL_ONCE.load(Ordering::Acquire) {
|
|
109
|
+
FAIL_ONCE.store(false, Ordering::Release);
|
|
110
|
+
panic!("Enchi is hungry!");
|
|
111
|
+
}
|
|
112
|
+
assert!(ctx.patched(MY_PATCH_ID));
|
|
113
|
+
ctx.timer(Duration::from_millis(1)).await;
|
|
114
|
+
Ok(().into())
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
starter.start_with_worker(wf_name, &mut worker).await;
|
|
118
|
+
worker.run_until_done().await.unwrap();
|
|
119
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
use crate::integ_tests::workflow_tests::patches::changes_wf;
|
|
1
2
|
use assert_matches::assert_matches;
|
|
2
3
|
use parking_lot::Mutex;
|
|
3
4
|
use std::{collections::HashSet, sync::Arc, time::Duration};
|
|
@@ -10,6 +11,7 @@ use temporal_sdk_core_protos::{
|
|
|
10
11
|
workflow_commands::{ScheduleActivity, StartTimer},
|
|
11
12
|
workflow_completion::WorkflowActivationCompletion,
|
|
12
13
|
},
|
|
14
|
+
temporal::api::enums::v1::EventType,
|
|
13
15
|
TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE,
|
|
14
16
|
};
|
|
15
17
|
use temporal_sdk_core_test_utils::{
|
|
@@ -129,6 +131,23 @@ async fn replay_using_wf_function() {
|
|
|
129
131
|
worker.run().await.unwrap();
|
|
130
132
|
}
|
|
131
133
|
|
|
134
|
+
#[tokio::test]
|
|
135
|
+
async fn replay_ending_wft_complete_with_commands_but_no_scheduled_started() {
|
|
136
|
+
let mut t = TestHistoryBuilder::default();
|
|
137
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
138
|
+
t.add_full_wf_task();
|
|
139
|
+
|
|
140
|
+
for i in 1..=2 {
|
|
141
|
+
let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
|
|
142
|
+
t.add_timer_fired(timer_started_event_id, i.to_string());
|
|
143
|
+
t.add_full_wf_task();
|
|
144
|
+
}
|
|
145
|
+
let func = timers_wf(3);
|
|
146
|
+
let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
|
|
147
|
+
worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
|
|
148
|
+
worker.run().await.unwrap();
|
|
149
|
+
}
|
|
150
|
+
|
|
132
151
|
async fn replay_abrupt_ending(t: TestHistoryBuilder) {
|
|
133
152
|
let func = timers_wf(1);
|
|
134
153
|
let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
|
|
@@ -205,6 +224,51 @@ async fn multiple_histories_can_handle_dupe_run_ids() {
|
|
|
205
224
|
worker.run().await.unwrap();
|
|
206
225
|
}
|
|
207
226
|
|
|
227
|
+
// Verifies SDK can decode patch markers before changing them to use json encoding
|
|
228
|
+
#[tokio::test]
|
|
229
|
+
async fn replay_old_patch_format() {
|
|
230
|
+
let mut worker = replay_sdk_worker([HistoryForReplay::new(
|
|
231
|
+
history_from_proto_binary("histories/old_change_marker_format.bin")
|
|
232
|
+
.await
|
|
233
|
+
.unwrap(),
|
|
234
|
+
"fake".to_owned(),
|
|
235
|
+
)]);
|
|
236
|
+
worker.register_wf("writes_change_markers", changes_wf);
|
|
237
|
+
worker.run().await.unwrap();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
#[tokio::test]
|
|
241
|
+
async fn replay_ends_with_empty_wft() {
|
|
242
|
+
let core = init_core_replay_preloaded(
|
|
243
|
+
"SayHelloWorkflow",
|
|
244
|
+
[HistoryForReplay::new(
|
|
245
|
+
history_from_proto_binary("histories/ends_empty_wft_complete.bin")
|
|
246
|
+
.await
|
|
247
|
+
.unwrap(),
|
|
248
|
+
"fake".to_owned(),
|
|
249
|
+
)],
|
|
250
|
+
);
|
|
251
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
252
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
|
|
253
|
+
task.run_id,
|
|
254
|
+
vec![ScheduleActivity {
|
|
255
|
+
seq: 1,
|
|
256
|
+
activity_id: "1".to_string(),
|
|
257
|
+
activity_type: "say_hello".to_string(),
|
|
258
|
+
..Default::default()
|
|
259
|
+
}
|
|
260
|
+
.into()],
|
|
261
|
+
))
|
|
262
|
+
.await
|
|
263
|
+
.unwrap();
|
|
264
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
265
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::empty(task.run_id))
|
|
266
|
+
.await
|
|
267
|
+
.unwrap();
|
|
268
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
269
|
+
assert!(task.eviction_reason().is_some());
|
|
270
|
+
}
|
|
271
|
+
|
|
208
272
|
fn timers_wf(num_timers: u32) -> WorkflowFunction {
|
|
209
273
|
WorkflowFunction::new(move |ctx: WfContext| async move {
|
|
210
274
|
for _ in 1..=num_timers {
|
|
@@ -14,6 +14,7 @@ const POST_RESET_SIG: &str = "post-reset";
|
|
|
14
14
|
async fn reset_workflow() {
|
|
15
15
|
let wf_name = "reset_me_wf";
|
|
16
16
|
let mut starter = CoreWfStarter::new(wf_name);
|
|
17
|
+
starter.no_remote_activities();
|
|
17
18
|
let mut worker = starter.worker().await;
|
|
18
19
|
worker.fetch_results = false;
|
|
19
20
|
let notify = Arc::new(Notify::new());
|