@temporalio/core-bridge 0.20.2 → 0.22.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 +137 -127
- package/index.d.ts +7 -2
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/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/docker-compose.yaml +5 -4
- package/sdk-core/client/Cargo.toml +1 -0
- package/sdk-core/client/src/lib.rs +52 -9
- package/sdk-core/client/src/raw.rs +9 -1
- package/sdk-core/client/src/retry.rs +12 -1
- package/sdk-core/client/src/workflow_handle/mod.rs +183 -0
- package/sdk-core/core/src/abstractions.rs +10 -3
- package/sdk-core/core/src/core_tests/child_workflows.rs +7 -9
- package/sdk-core/core/src/core_tests/determinism.rs +8 -19
- package/sdk-core/core/src/core_tests/local_activities.rs +22 -32
- package/sdk-core/core/src/core_tests/queries.rs +272 -5
- package/sdk-core/core/src/core_tests/workers.rs +4 -34
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +197 -41
- package/sdk-core/core/src/pending_activations.rs +11 -0
- package/sdk-core/core/src/telemetry/mod.rs +1 -1
- package/sdk-core/core/src/test_help/mod.rs +57 -7
- package/sdk-core/core/src/worker/mod.rs +64 -15
- package/sdk-core/core/src/workflow/machines/mod.rs +1 -1
- package/sdk-core/core/src/workflow/machines/timer_state_machine.rs +2 -2
- package/sdk-core/core/src/workflow/machines/workflow_machines.rs +14 -3
- package/sdk-core/core/src/workflow/mod.rs +5 -2
- package/sdk-core/core/src/workflow/workflow_tasks/cache_manager.rs +47 -2
- package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +16 -2
- package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +252 -125
- package/sdk-core/core-api/src/worker.rs +9 -0
- package/sdk-core/sdk/Cargo.toml +1 -0
- package/sdk-core/sdk/src/activity_context.rs +223 -0
- package/sdk-core/sdk/src/interceptors.rs +8 -2
- package/sdk-core/sdk/src/lib.rs +167 -122
- package/sdk-core/sdk-core-protos/src/history_info.rs +3 -7
- package/sdk-core/test-utils/Cargo.toml +1 -0
- package/sdk-core/test-utils/src/lib.rs +78 -37
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +11 -4
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +0 -1
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +0 -3
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +33 -17
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +10 -1
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +0 -1
- package/sdk-core/tests/integ_tests/workflow_tests.rs +71 -3
- package/sdk-core/tests/load_tests.rs +80 -6
- package/src/errors.rs +9 -2
- package/src/lib.rs +39 -16
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
replay::{default_wes_attribs, TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE},
|
|
3
|
-
test_help::{
|
|
3
|
+
test_help::{mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
|
|
4
4
|
worker::client::mocks::mock_workflow_client,
|
|
5
5
|
};
|
|
6
6
|
use anyhow::anyhow;
|
|
7
7
|
use futures::future::join_all;
|
|
8
8
|
use std::{
|
|
9
|
-
sync::{
|
|
10
|
-
atomic::{AtomicUsize, Ordering},
|
|
11
|
-
Arc,
|
|
12
|
-
},
|
|
9
|
+
sync::atomic::{AtomicUsize, Ordering},
|
|
13
10
|
time::Duration,
|
|
14
11
|
};
|
|
15
12
|
use temporal_client::WorkflowOptions;
|
|
16
|
-
use temporal_sdk::{LocalActivityOptions, WfContext, WorkflowResult};
|
|
13
|
+
use temporal_sdk::{ActContext, LocalActivityOptions, WfContext, WorkflowResult};
|
|
17
14
|
use temporal_sdk_core_protos::{
|
|
18
15
|
coresdk::{common::RetryPolicy, AsJsonPayloadExt},
|
|
19
16
|
temporal::api::{enums::v1::EventType, failure::v1::Failure},
|
|
20
17
|
};
|
|
21
|
-
use temporal_sdk_core_test_utils::TestWorker;
|
|
22
18
|
use tokio::sync::Barrier;
|
|
23
19
|
|
|
24
|
-
async fn echo(e: String) -> anyhow::Result<String> {
|
|
20
|
+
async fn echo(_ctx: ActContext, e: String) -> anyhow::Result<String> {
|
|
25
21
|
Ok(e)
|
|
26
22
|
}
|
|
27
23
|
|
|
@@ -52,12 +48,11 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
|
|
|
52
48
|
vec![1.into(), 2.into(), ResponseType::AllHistory]
|
|
53
49
|
};
|
|
54
50
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, resps, mock);
|
|
55
|
-
let mut
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
51
|
+
let mut worker = mock_sdk_cfg(mh, |cfg| {
|
|
52
|
+
if cached {
|
|
53
|
+
cfg.max_cached_workflows = 1;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
61
56
|
|
|
62
57
|
worker.register_wf(
|
|
63
58
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
@@ -119,12 +114,13 @@ async fn local_act_many_concurrent() {
|
|
|
119
114
|
let wf_id = "fakeid";
|
|
120
115
|
let mock = mock_workflow_client();
|
|
121
116
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
|
|
122
|
-
let
|
|
123
|
-
let core = mock_worker(mock);
|
|
124
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
117
|
+
let mut worker = mock_sdk(mh);
|
|
125
118
|
|
|
126
119
|
worker.register_wf(DEFAULT_WORKFLOW_TYPE.to_owned(), local_act_fanout_wf);
|
|
127
|
-
worker.register_activity(
|
|
120
|
+
worker.register_activity(
|
|
121
|
+
"echo",
|
|
122
|
+
|_ctx: ActContext, str: String| async move { Ok(str) },
|
|
123
|
+
);
|
|
128
124
|
worker
|
|
129
125
|
.submit_wf(
|
|
130
126
|
wf_id.to_owned(),
|
|
@@ -167,10 +163,9 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
167
163
|
// and might poll an extra time
|
|
168
164
|
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 2, 2, 2], mock);
|
|
169
165
|
mh.enforce_correct_number_of_polls = false;
|
|
170
|
-
let mut
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
let mut worker = TestWorker::new(core.clone(), TEST_Q.to_string());
|
|
166
|
+
let mut worker = mock_sdk_cfg(mh, |wc| wc.max_cached_workflows = 1);
|
|
167
|
+
let core = worker.orig_core_worker.clone();
|
|
168
|
+
|
|
174
169
|
let shutdown_barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
|
|
175
170
|
|
|
176
171
|
worker.register_wf(
|
|
@@ -185,7 +180,7 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
185
180
|
Ok(().into())
|
|
186
181
|
},
|
|
187
182
|
);
|
|
188
|
-
worker.register_activity("echo", move |str: String| async move {
|
|
183
|
+
worker.register_activity("echo", move |_ctx: ActContext, str: String| async move {
|
|
189
184
|
if shutdown_middle {
|
|
190
185
|
shutdown_barr.wait().await;
|
|
191
186
|
}
|
|
@@ -226,9 +221,7 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
|
226
221
|
let wf_id = "fakeid";
|
|
227
222
|
let mock = mock_workflow_client();
|
|
228
223
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
|
|
229
|
-
let
|
|
230
|
-
let core = mock_worker(mock);
|
|
231
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
224
|
+
let mut worker = mock_sdk(mh);
|
|
232
225
|
|
|
233
226
|
worker.register_wf(
|
|
234
227
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
@@ -256,7 +249,7 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
|
256
249
|
},
|
|
257
250
|
);
|
|
258
251
|
let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
|
|
259
|
-
worker.register_activity("echo", move |_: String| async move {
|
|
252
|
+
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
260
253
|
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
261
254
|
if 2 == attempts.fetch_add(1, Ordering::Relaxed) && eventually_pass {
|
|
262
255
|
Ok(())
|
|
@@ -309,10 +302,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
309
302
|
[1.into(), 2.into(), ResponseType::AllHistory],
|
|
310
303
|
mock,
|
|
311
304
|
);
|
|
312
|
-
let mut
|
|
313
|
-
mock.worker_cfg(|w| w.max_cached_workflows = 1);
|
|
314
|
-
let core = mock_worker(mock);
|
|
315
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
305
|
+
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
316
306
|
|
|
317
307
|
worker.register_wf(
|
|
318
308
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
@@ -338,7 +328,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
338
328
|
Ok(().into())
|
|
339
329
|
},
|
|
340
330
|
);
|
|
341
|
-
worker.register_activity("echo", move |_: String| async move {
|
|
331
|
+
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
342
332
|
Result::<(), _>::Err(anyhow!("Oh no I failed!"))
|
|
343
333
|
});
|
|
344
334
|
worker
|
|
@@ -11,8 +11,13 @@ use std::{
|
|
|
11
11
|
use temporal_sdk_core_api::Worker as WorkerTrait;
|
|
12
12
|
use temporal_sdk_core_protos::{
|
|
13
13
|
coresdk::{
|
|
14
|
-
workflow_activation::{
|
|
15
|
-
|
|
14
|
+
workflow_activation::{
|
|
15
|
+
remove_from_cache::EvictionReason, workflow_activation_job, WorkflowActivationJob,
|
|
16
|
+
},
|
|
17
|
+
workflow_commands::{
|
|
18
|
+
ActivityCancellationType, CompleteWorkflowExecution, ContinueAsNewWorkflowExecution,
|
|
19
|
+
QueryResult, QuerySuccess, RequestCancelActivity,
|
|
20
|
+
},
|
|
16
21
|
workflow_completion::WorkflowActivationCompletion,
|
|
17
22
|
},
|
|
18
23
|
temporal::api::{
|
|
@@ -21,11 +26,12 @@ use temporal_sdk_core_protos::{
|
|
|
21
26
|
history::v1::History,
|
|
22
27
|
query::v1::WorkflowQuery,
|
|
23
28
|
workflowservice::v1::{
|
|
24
|
-
|
|
29
|
+
GetWorkflowExecutionHistoryResponse, RespondQueryTaskCompletedResponse,
|
|
30
|
+
RespondWorkflowTaskCompletedResponse,
|
|
25
31
|
},
|
|
26
32
|
},
|
|
27
33
|
};
|
|
28
|
-
use temporal_sdk_core_test_utils::start_timer_cmd;
|
|
34
|
+
use temporal_sdk_core_test_utils::{schedule_activity_cmd, start_timer_cmd};
|
|
29
35
|
|
|
30
36
|
#[rstest::rstest]
|
|
31
37
|
#[case::with_history(true)]
|
|
@@ -80,6 +86,10 @@ async fn legacy_query(#[case] include_history: bool) {
|
|
|
80
86
|
};
|
|
81
87
|
let clear_eviction = || async {
|
|
82
88
|
let t = worker.poll_workflow_activation().await.unwrap();
|
|
89
|
+
assert_matches!(
|
|
90
|
+
t.jobs[0].variant,
|
|
91
|
+
Some(workflow_activation_job::Variant::RemoveFromCache(_))
|
|
92
|
+
);
|
|
83
93
|
worker
|
|
84
94
|
.complete_workflow_activation(WorkflowActivationCompletion::empty(t.run_id))
|
|
85
95
|
.await
|
|
@@ -155,7 +165,12 @@ async fn new_queries(#[case] num_queries: usize) {
|
|
|
155
165
|
let tasks = VecDeque::from(vec![
|
|
156
166
|
hist_to_poll_resp(&t, wfid.to_owned(), 1.into(), TEST_Q.to_string()),
|
|
157
167
|
{
|
|
158
|
-
let mut pr = hist_to_poll_resp(
|
|
168
|
+
let mut pr = hist_to_poll_resp(
|
|
169
|
+
&t,
|
|
170
|
+
wfid.to_owned(),
|
|
171
|
+
ResponseType::OneTask(2),
|
|
172
|
+
TEST_Q.to_string(),
|
|
173
|
+
);
|
|
159
174
|
pr.queries = HashMap::new();
|
|
160
175
|
for i in 1..=num_queries {
|
|
161
176
|
pr.queries.insert(
|
|
@@ -381,3 +396,255 @@ async fn legacy_query_after_complete(#[values(false, true)] full_history: bool)
|
|
|
381
396
|
|
|
382
397
|
core.shutdown().await;
|
|
383
398
|
}
|
|
399
|
+
|
|
400
|
+
enum QueryHists {
|
|
401
|
+
Empty,
|
|
402
|
+
Full,
|
|
403
|
+
Partial,
|
|
404
|
+
}
|
|
405
|
+
#[rstest::rstest]
|
|
406
|
+
#[tokio::test]
|
|
407
|
+
async fn query_cache_miss_causes_page_fetch_dont_reply_wft_too_early(
|
|
408
|
+
#[values(QueryHists::Empty, QueryHists::Full, QueryHists::Partial)] hist_type: QueryHists,
|
|
409
|
+
) {
|
|
410
|
+
let wfid = "fake_wf_id";
|
|
411
|
+
let query_resp = "response";
|
|
412
|
+
let t = canned_histories::single_timer("1");
|
|
413
|
+
let full_hist = t.get_full_history_info().unwrap();
|
|
414
|
+
let tasks = VecDeque::from(vec![{
|
|
415
|
+
let mut pr = match hist_type {
|
|
416
|
+
QueryHists::Empty => {
|
|
417
|
+
// Create a no-history poll response. This happens to be easiest to do by just ripping
|
|
418
|
+
// out the history after making a normal one.
|
|
419
|
+
let mut pr = hist_to_poll_resp(
|
|
420
|
+
&t,
|
|
421
|
+
wfid.to_owned(),
|
|
422
|
+
ResponseType::AllHistory,
|
|
423
|
+
TEST_Q.to_string(),
|
|
424
|
+
);
|
|
425
|
+
pr.history = Some(Default::default());
|
|
426
|
+
pr
|
|
427
|
+
}
|
|
428
|
+
QueryHists::Full => hist_to_poll_resp(
|
|
429
|
+
&t,
|
|
430
|
+
wfid.to_owned(),
|
|
431
|
+
ResponseType::AllHistory,
|
|
432
|
+
TEST_Q.to_string(),
|
|
433
|
+
),
|
|
434
|
+
QueryHists::Partial => {
|
|
435
|
+
// Create a partial task
|
|
436
|
+
hist_to_poll_resp(
|
|
437
|
+
&t,
|
|
438
|
+
wfid.to_owned(),
|
|
439
|
+
ResponseType::OneTask(2),
|
|
440
|
+
TEST_Q.to_string(),
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
pr.queries = HashMap::new();
|
|
445
|
+
pr.queries.insert(
|
|
446
|
+
"the-query".to_string(),
|
|
447
|
+
WorkflowQuery {
|
|
448
|
+
query_type: "query-type".to_string(),
|
|
449
|
+
query_args: Some(b"hi".into()),
|
|
450
|
+
header: None,
|
|
451
|
+
},
|
|
452
|
+
);
|
|
453
|
+
pr
|
|
454
|
+
}]);
|
|
455
|
+
let mut mock_client = mock_workflow_client();
|
|
456
|
+
if !matches!(hist_type, QueryHists::Full) {
|
|
457
|
+
mock_client
|
|
458
|
+
.expect_get_workflow_execution_history()
|
|
459
|
+
.returning(move |_, _, _| {
|
|
460
|
+
Ok(GetWorkflowExecutionHistoryResponse {
|
|
461
|
+
history: Some(full_hist.clone().into()),
|
|
462
|
+
..Default::default()
|
|
463
|
+
})
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
mock_client
|
|
467
|
+
.expect_complete_workflow_task()
|
|
468
|
+
.times(1)
|
|
469
|
+
.returning(|resp| {
|
|
470
|
+
// Verify both the complete command and the query response are sent
|
|
471
|
+
assert_eq!(resp.commands.len(), 1);
|
|
472
|
+
assert_eq!(resp.query_responses.len(), 1);
|
|
473
|
+
|
|
474
|
+
Ok(RespondWorkflowTaskCompletedResponse::default())
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
let mut mock = MocksHolder::from_client_with_responses(mock_client, tasks, vec![]);
|
|
478
|
+
mock.worker_cfg(|wc| wc.max_cached_workflows = 10);
|
|
479
|
+
let core = mock_worker(mock);
|
|
480
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
481
|
+
// The first task should *only* start the workflow. It should *not* have a query in it, which
|
|
482
|
+
// was the bug. Query should only appear after we have caught up on replay.
|
|
483
|
+
assert_matches!(
|
|
484
|
+
task.jobs.as_slice(),
|
|
485
|
+
[WorkflowActivationJob {
|
|
486
|
+
variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
|
|
487
|
+
}]
|
|
488
|
+
);
|
|
489
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
490
|
+
task.run_id,
|
|
491
|
+
start_timer_cmd(1, Duration::from_secs(1)),
|
|
492
|
+
))
|
|
493
|
+
.await
|
|
494
|
+
.unwrap();
|
|
495
|
+
|
|
496
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
497
|
+
assert_matches!(
|
|
498
|
+
task.jobs.as_slice(),
|
|
499
|
+
[WorkflowActivationJob {
|
|
500
|
+
variant: Some(workflow_activation_job::Variant::FireTimer(_)),
|
|
501
|
+
}]
|
|
502
|
+
);
|
|
503
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
504
|
+
task.run_id,
|
|
505
|
+
CompleteWorkflowExecution { result: None }.into(),
|
|
506
|
+
))
|
|
507
|
+
.await
|
|
508
|
+
.unwrap();
|
|
509
|
+
|
|
510
|
+
// Now the query shall arrive
|
|
511
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
512
|
+
assert_matches!(
|
|
513
|
+
task.jobs[0],
|
|
514
|
+
WorkflowActivationJob {
|
|
515
|
+
variant: Some(workflow_activation_job::Variant::QueryWorkflow(_)),
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
519
|
+
task.run_id,
|
|
520
|
+
QueryResult {
|
|
521
|
+
query_id: "the-query".to_string(),
|
|
522
|
+
variant: Some(
|
|
523
|
+
QuerySuccess {
|
|
524
|
+
response: Some(query_resp.into()),
|
|
525
|
+
}
|
|
526
|
+
.into(),
|
|
527
|
+
),
|
|
528
|
+
}
|
|
529
|
+
.into(),
|
|
530
|
+
))
|
|
531
|
+
.await
|
|
532
|
+
.unwrap();
|
|
533
|
+
|
|
534
|
+
core.shutdown().await;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
#[tokio::test]
|
|
538
|
+
async fn query_replay_with_continue_as_new_doesnt_reply_empty_command() {
|
|
539
|
+
let wfid = "fake_wf_id";
|
|
540
|
+
let t = canned_histories::single_timer("1");
|
|
541
|
+
let query_with_hist_task = {
|
|
542
|
+
let mut pr = hist_to_poll_resp(
|
|
543
|
+
&t,
|
|
544
|
+
wfid.to_owned(),
|
|
545
|
+
ResponseType::ToTaskNum(1),
|
|
546
|
+
TEST_Q.to_string(),
|
|
547
|
+
);
|
|
548
|
+
pr.queries = HashMap::new();
|
|
549
|
+
pr.queries.insert(
|
|
550
|
+
"the-query".to_string(),
|
|
551
|
+
WorkflowQuery {
|
|
552
|
+
query_type: "query-type".to_string(),
|
|
553
|
+
query_args: Some(b"hi".into()),
|
|
554
|
+
header: None,
|
|
555
|
+
},
|
|
556
|
+
);
|
|
557
|
+
pr
|
|
558
|
+
};
|
|
559
|
+
let tasks = VecDeque::from(vec![query_with_hist_task]);
|
|
560
|
+
let mut mock_client = mock_workflow_client();
|
|
561
|
+
mock_client
|
|
562
|
+
.expect_complete_workflow_task()
|
|
563
|
+
.times(1)
|
|
564
|
+
.returning(|resp| {
|
|
565
|
+
// Verify both the complete command and the query response are sent
|
|
566
|
+
assert_eq!(resp.commands.len(), 1);
|
|
567
|
+
assert_eq!(resp.query_responses.len(), 1);
|
|
568
|
+
Ok(RespondWorkflowTaskCompletedResponse::default())
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
let mut mock = MocksHolder::from_client_with_responses(mock_client, tasks, vec![]);
|
|
572
|
+
mock.worker_cfg(|wc| wc.max_cached_workflows = 10);
|
|
573
|
+
let core = mock_worker(mock);
|
|
574
|
+
|
|
575
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
576
|
+
// Scheduling and immediately canceling an activity produces another activation which is
|
|
577
|
+
// important in this repro
|
|
578
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
|
|
579
|
+
task.run_id,
|
|
580
|
+
vec![
|
|
581
|
+
schedule_activity_cmd(
|
|
582
|
+
0,
|
|
583
|
+
"whatever",
|
|
584
|
+
"act-id",
|
|
585
|
+
ActivityCancellationType::TryCancel,
|
|
586
|
+
Duration::from_secs(60),
|
|
587
|
+
Duration::from_secs(60),
|
|
588
|
+
),
|
|
589
|
+
RequestCancelActivity { seq: 0 }.into(),
|
|
590
|
+
ContinueAsNewWorkflowExecution {
|
|
591
|
+
..Default::default()
|
|
592
|
+
}
|
|
593
|
+
.into(),
|
|
594
|
+
],
|
|
595
|
+
))
|
|
596
|
+
.await
|
|
597
|
+
.unwrap();
|
|
598
|
+
|
|
599
|
+
// Activity unblocked
|
|
600
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
601
|
+
assert_matches!(
|
|
602
|
+
task.jobs.as_slice(),
|
|
603
|
+
[WorkflowActivationJob {
|
|
604
|
+
variant: Some(workflow_activation_job::Variant::ResolveActivity(_)),
|
|
605
|
+
}]
|
|
606
|
+
);
|
|
607
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::empty(task.run_id))
|
|
608
|
+
.await
|
|
609
|
+
.unwrap();
|
|
610
|
+
|
|
611
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
612
|
+
let query = assert_matches!(
|
|
613
|
+
task.jobs.as_slice(),
|
|
614
|
+
[WorkflowActivationJob {
|
|
615
|
+
variant: Some(workflow_activation_job::Variant::QueryWorkflow(q)),
|
|
616
|
+
}] => q
|
|
617
|
+
);
|
|
618
|
+
// Throw an evict in there. Repro required a pending eviction during complete.
|
|
619
|
+
core.request_wf_eviction(&task.run_id, "I said so", EvictionReason::LangRequested);
|
|
620
|
+
|
|
621
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
622
|
+
task.run_id,
|
|
623
|
+
QueryResult {
|
|
624
|
+
query_id: query.query_id.clone(),
|
|
625
|
+
variant: Some(
|
|
626
|
+
QuerySuccess {
|
|
627
|
+
response: Some("whatever".into()),
|
|
628
|
+
}
|
|
629
|
+
.into(),
|
|
630
|
+
),
|
|
631
|
+
}
|
|
632
|
+
.into(),
|
|
633
|
+
))
|
|
634
|
+
.await
|
|
635
|
+
.unwrap();
|
|
636
|
+
|
|
637
|
+
// Need to complete the eviction to finally send commands and finish
|
|
638
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
639
|
+
assert_matches!(
|
|
640
|
+
task.jobs.as_slice(),
|
|
641
|
+
[WorkflowActivationJob {
|
|
642
|
+
variant: Some(workflow_activation_job::Variant::RemoveFromCache(_))
|
|
643
|
+
}]
|
|
644
|
+
);
|
|
645
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::empty(task.run_id))
|
|
646
|
+
.await
|
|
647
|
+
.unwrap();
|
|
648
|
+
|
|
649
|
+
core.shutdown().await;
|
|
650
|
+
}
|
|
@@ -20,38 +20,6 @@ use temporal_sdk_core_protos::{
|
|
|
20
20
|
use temporal_sdk_core_test_utils::start_timer_cmd;
|
|
21
21
|
use tokio::sync::{watch, Barrier};
|
|
22
22
|
|
|
23
|
-
#[tokio::test]
|
|
24
|
-
async fn multi_workers() {
|
|
25
|
-
// TODO: Turn this into a test with multiple independent workers
|
|
26
|
-
// Make histories for 5 different workflows on 5 different task queues
|
|
27
|
-
// let hists = (0..5).into_iter().map(|i| {
|
|
28
|
-
// let wf_id = format!("fake-wf-{}", i);
|
|
29
|
-
// let hist = canned_histories::single_timer("1");
|
|
30
|
-
// FakeWfResponses {
|
|
31
|
-
// wf_id,
|
|
32
|
-
// hist,
|
|
33
|
-
// response_batches: vec![1.into(), 2.into()],
|
|
34
|
-
// task_q: format!("q-{}", i),
|
|
35
|
-
// }
|
|
36
|
-
// });
|
|
37
|
-
// let mock = build_multihist_mock_sg(hists, false, None);
|
|
38
|
-
//
|
|
39
|
-
// let core = &mock_worker(mock);
|
|
40
|
-
//
|
|
41
|
-
// for i in 0..5 {
|
|
42
|
-
// let tq = format!("q-{}", i);
|
|
43
|
-
// let res = core.poll_workflow_activation().await.unwrap();
|
|
44
|
-
// assert_matches!(
|
|
45
|
-
// res.jobs[0].variant,
|
|
46
|
-
// Some(workflow_activation_job::Variant::StartWorkflow(_))
|
|
47
|
-
// );
|
|
48
|
-
// core.complete_workflow_activation(WorkflowActivationCompletion::empty(res.run_id))
|
|
49
|
-
// .await
|
|
50
|
-
// .unwrap();
|
|
51
|
-
// }
|
|
52
|
-
// core.shutdown().await;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
23
|
#[tokio::test]
|
|
56
24
|
async fn after_shutdown_of_worker_get_shutdown_err() {
|
|
57
25
|
let t = canned_histories::single_timer("1");
|
|
@@ -195,7 +163,7 @@ async fn can_shutdown_local_act_only_worker_when_act_polling() {
|
|
|
195
163
|
}
|
|
196
164
|
|
|
197
165
|
#[tokio::test]
|
|
198
|
-
async fn
|
|
166
|
+
async fn complete_with_task_not_found_during_shutdown() {
|
|
199
167
|
let t = canned_histories::single_timer("1");
|
|
200
168
|
let mut mock = mock_workflow_client();
|
|
201
169
|
mock.expect_complete_workflow_task()
|
|
@@ -236,5 +204,7 @@ async fn complete_with_task_not_found_during_shutdwn() {
|
|
|
236
204
|
complete_order.borrow_mut().push(1);
|
|
237
205
|
};
|
|
238
206
|
tokio::join!(shutdown_fut, poll_fut, complete_fut);
|
|
239
|
-
|
|
207
|
+
// Shutdown will currently complete first before the actual eviction reply since the
|
|
208
|
+
// workflow task is marked complete as soon as we get not found back from the server.
|
|
209
|
+
assert_eq!(&complete_order.into_inner(), &[1, 3, 2])
|
|
240
210
|
}
|