@temporalio/core-bridge 0.19.2 → 0.20.2
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 +90 -157
- package/Cargo.toml +1 -0
- package/index.d.ts +11 -27
- package/package.json +3 -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 +1 -1
- package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
- package/sdk-core/.cargo/config.toml +1 -0
- package/sdk-core/CODEOWNERS +1 -1
- package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +119 -86
- package/sdk-core/bridge-ffi/src/lib.rs +311 -315
- package/sdk-core/bridge-ffi/src/wrappers.rs +108 -113
- package/sdk-core/client/Cargo.toml +13 -9
- package/sdk-core/client/LICENSE.txt +23 -0
- package/sdk-core/client/src/lib.rs +286 -174
- package/sdk-core/client/src/metrics.rs +86 -12
- package/sdk-core/client/src/raw.rs +566 -0
- package/sdk-core/client/src/retry.rs +137 -99
- package/sdk-core/core/Cargo.toml +15 -10
- package/sdk-core/core/LICENSE.txt +23 -0
- package/sdk-core/core/benches/workflow_replay.rs +79 -0
- package/sdk-core/core/src/abstractions.rs +38 -0
- package/sdk-core/core/src/core_tests/activity_tasks.rs +108 -182
- package/sdk-core/core/src/core_tests/child_workflows.rs +16 -11
- package/sdk-core/core/src/core_tests/determinism.rs +24 -12
- package/sdk-core/core/src/core_tests/local_activities.rs +53 -27
- package/sdk-core/core/src/core_tests/mod.rs +30 -43
- package/sdk-core/core/src/core_tests/queries.rs +82 -81
- package/sdk-core/core/src/core_tests/workers.rs +111 -296
- package/sdk-core/core/src/core_tests/workflow_cancels.rs +4 -4
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +257 -242
- package/sdk-core/core/src/lib.rs +73 -318
- package/sdk-core/core/src/pollers/mod.rs +4 -6
- package/sdk-core/core/src/pollers/poll_buffer.rs +20 -14
- package/sdk-core/core/src/protosext/mod.rs +7 -10
- package/sdk-core/core/src/replay/mod.rs +11 -150
- package/sdk-core/core/src/telemetry/metrics.rs +35 -2
- package/sdk-core/core/src/telemetry/mod.rs +49 -16
- package/sdk-core/core/src/telemetry/prometheus_server.rs +14 -35
- package/sdk-core/core/src/test_help/mod.rs +104 -170
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +57 -34
- package/sdk-core/core/src/worker/activities/local_activities.rs +95 -23
- package/sdk-core/core/src/worker/activities.rs +23 -16
- package/sdk-core/core/src/worker/client/mocks.rs +86 -0
- package/sdk-core/core/src/worker/client.rs +209 -0
- package/sdk-core/core/src/worker/mod.rs +207 -108
- package/sdk-core/core/src/workflow/driven_workflow.rs +21 -6
- package/sdk-core/core/src/workflow/history_update.rs +107 -24
- package/sdk-core/core/src/workflow/machines/activity_state_machine.rs +2 -3
- package/sdk-core/core/src/workflow/machines/child_workflow_state_machine.rs +2 -3
- package/sdk-core/core/src/workflow/machines/mod.rs +20 -17
- package/sdk-core/core/src/workflow/machines/signal_external_state_machine.rs +56 -19
- package/sdk-core/core/src/workflow/machines/transition_coverage.rs +5 -0
- package/sdk-core/core/src/workflow/machines/upsert_search_attributes_state_machine.rs +230 -22
- package/sdk-core/core/src/workflow/machines/workflow_machines.rs +81 -115
- package/sdk-core/core/src/workflow/machines/workflow_task_state_machine.rs +4 -4
- package/sdk-core/core/src/workflow/mod.rs +13 -1
- package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +70 -11
- package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +65 -41
- package/sdk-core/core-api/Cargo.toml +9 -1
- package/sdk-core/core-api/LICENSE.txt +23 -0
- package/sdk-core/core-api/src/errors.rs +7 -38
- package/sdk-core/core-api/src/lib.rs +44 -52
- package/sdk-core/core-api/src/worker.rs +10 -2
- package/sdk-core/etc/deps.svg +127 -96
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -7
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +10 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +6 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +2 -1
- package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +12 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +25 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +19 -35
- package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -6
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +53 -11
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +14 -7
- package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +3 -5
- package/sdk-core/sdk/Cargo.toml +16 -2
- package/sdk-core/sdk/LICENSE.txt +23 -0
- package/sdk-core/sdk/src/interceptors.rs +11 -0
- package/sdk-core/sdk/src/lib.rs +139 -151
- package/sdk-core/sdk/src/workflow_context/options.rs +86 -1
- package/sdk-core/sdk/src/workflow_context.rs +36 -17
- package/sdk-core/sdk/src/workflow_future.rs +19 -25
- package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
- package/sdk-core/sdk-core-protos/build.rs +1 -0
- package/sdk-core/sdk-core-protos/src/history_info.rs +17 -4
- package/sdk-core/sdk-core-protos/src/lib.rs +251 -47
- package/sdk-core/test-utils/Cargo.toml +3 -1
- package/sdk-core/test-utils/src/canned_histories.rs +27 -0
- package/sdk-core/test-utils/src/histfetch.rs +3 -3
- package/sdk-core/test-utils/src/lib.rs +223 -68
- package/sdk-core/tests/integ_tests/client_tests.rs +27 -4
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +93 -14
- package/sdk-core/tests/integ_tests/polling_tests.rs +18 -12
- package/sdk-core/tests/integ_tests/queries_tests.rs +50 -53
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +117 -103
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +8 -1
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +10 -5
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +7 -1
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +32 -9
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +7 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +76 -15
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +19 -3
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +39 -42
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +84 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +30 -8
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +21 -6
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +26 -16
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +66 -0
- package/sdk-core/tests/integ_tests/workflow_tests.rs +78 -74
- package/sdk-core/tests/load_tests.rs +9 -6
- package/sdk-core/tests/main.rs +43 -10
- package/src/conversions.rs +7 -12
- package/src/lib.rs +322 -357
- package/sdk-core/client/src/mocks.rs +0 -167
- package/sdk-core/core/src/worker/dispatcher.rs +0 -171
- package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +0 -61
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//! This module contains very generic helpers that can be used codebase-wide
|
|
2
|
+
|
|
3
|
+
use crate::MetricsContext;
|
|
4
|
+
use tokio::sync::{AcquireError, Semaphore, SemaphorePermit};
|
|
5
|
+
|
|
6
|
+
/// Wraps a [Semaphore] with a function call that is fed the available permits any time a permit is
|
|
7
|
+
/// acquired or restored through the provided methods
|
|
8
|
+
pub(crate) struct MeteredSemaphore {
|
|
9
|
+
pub sem: Semaphore,
|
|
10
|
+
metrics_ctx: MetricsContext,
|
|
11
|
+
record_fn: fn(&MetricsContext, usize),
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl MeteredSemaphore {
|
|
15
|
+
pub fn new(
|
|
16
|
+
inital_permits: usize,
|
|
17
|
+
metrics_ctx: MetricsContext,
|
|
18
|
+
record_fn: fn(&MetricsContext, usize),
|
|
19
|
+
) -> Self {
|
|
20
|
+
Self {
|
|
21
|
+
sem: Semaphore::new(inital_permits),
|
|
22
|
+
metrics_ctx,
|
|
23
|
+
record_fn,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
pub async fn acquire(&self) -> Result<SemaphorePermit<'_>, AcquireError> {
|
|
28
|
+
let res = self.sem.acquire().await;
|
|
29
|
+
(self.record_fn)(&self.metrics_ctx, self.sem.available_permits());
|
|
30
|
+
res
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Adds just one permit
|
|
34
|
+
pub fn add_permit(&self) {
|
|
35
|
+
self.sem.add_permits(1);
|
|
36
|
+
(self.record_fn)(&self.metrics_ctx, self.sem.available_permits());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -1,29 +1,26 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
job_assert,
|
|
3
3
|
test_help::{
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
poll_and_reply, MockWorker, MocksHolder, TEST_Q,
|
|
4
|
+
build_fake_worker, canned_histories, gen_assert_and_reply, mock_manual_poller, mock_poller,
|
|
5
|
+
mock_worker, poll_and_reply, test_worker_cfg, MockWorker, MocksHolder,
|
|
7
6
|
},
|
|
7
|
+
worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
|
|
8
8
|
workflow::WorkflowCachingPolicy::NonSticky,
|
|
9
|
-
ActivityHeartbeat,
|
|
9
|
+
ActivityHeartbeat, Worker, WorkerConfigBuilder,
|
|
10
10
|
};
|
|
11
11
|
use futures::FutureExt;
|
|
12
12
|
use std::{
|
|
13
13
|
cell::RefCell,
|
|
14
14
|
collections::{hash_map::Entry, HashMap, VecDeque},
|
|
15
15
|
rc::Rc,
|
|
16
|
-
sync::{
|
|
17
|
-
atomic::{AtomicUsize, Ordering},
|
|
18
|
-
Arc,
|
|
19
|
-
},
|
|
16
|
+
sync::atomic::{AtomicUsize, Ordering},
|
|
20
17
|
time::Duration,
|
|
21
18
|
};
|
|
22
|
-
use
|
|
19
|
+
use temporal_sdk_core_api::Worker as WorkerTrait;
|
|
23
20
|
use temporal_sdk_core_protos::{
|
|
24
21
|
coresdk::{
|
|
25
22
|
activity_result::{activity_resolution, ActivityExecutionResult, ActivityResolution},
|
|
26
|
-
activity_task::activity_task,
|
|
23
|
+
activity_task::{activity_task, ActivityTask},
|
|
27
24
|
workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
|
|
28
25
|
workflow_commands::{
|
|
29
26
|
ActivityCancellationType, CompleteWorkflowExecution, RequestCancelActivity,
|
|
@@ -38,11 +35,11 @@ use temporal_sdk_core_protos::{
|
|
|
38
35
|
},
|
|
39
36
|
};
|
|
40
37
|
use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd};
|
|
41
|
-
use tokio::{join,
|
|
38
|
+
use tokio::{join, time::sleep};
|
|
42
39
|
|
|
43
40
|
#[tokio::test]
|
|
44
41
|
async fn max_activities_respected() {
|
|
45
|
-
let
|
|
42
|
+
let _task_q = "q";
|
|
46
43
|
let mut tasks = VecDeque::from(vec![
|
|
47
44
|
PollActivityTaskQueueResponse {
|
|
48
45
|
task_token: vec![1],
|
|
@@ -60,47 +57,38 @@ async fn max_activities_respected() {
|
|
|
60
57
|
..Default::default()
|
|
61
58
|
},
|
|
62
59
|
]);
|
|
63
|
-
let mut
|
|
64
|
-
|
|
60
|
+
let mut mock_client = mock_workflow_client();
|
|
61
|
+
mock_client
|
|
65
62
|
.expect_poll_activity_task()
|
|
66
63
|
.times(3)
|
|
67
|
-
.returning(move |_| Ok(tasks.pop_front().unwrap()));
|
|
68
|
-
|
|
64
|
+
.returning(move |_, _| Ok(tasks.pop_front().unwrap()));
|
|
65
|
+
mock_client
|
|
69
66
|
.expect_complete_activity_task()
|
|
70
67
|
.returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
|
|
71
68
|
|
|
72
|
-
let
|
|
73
|
-
|
|
74
|
-
CoreInitOptionsBuilder::default()
|
|
75
|
-
.gateway_opts(fake_sg_opts())
|
|
76
|
-
.build()
|
|
77
|
-
.unwrap(),
|
|
78
|
-
);
|
|
79
|
-
core.register_worker(
|
|
80
|
-
WorkerConfigBuilder::default()
|
|
81
|
-
.task_queue(TEST_Q)
|
|
69
|
+
let worker = Worker::new_test(
|
|
70
|
+
test_worker_cfg()
|
|
82
71
|
.max_outstanding_activities(2_usize)
|
|
83
72
|
.build()
|
|
84
73
|
.unwrap(),
|
|
85
|
-
|
|
86
|
-
|
|
74
|
+
mock_client,
|
|
75
|
+
);
|
|
87
76
|
|
|
88
77
|
// We allow two outstanding activities, therefore first two polls should return right away
|
|
89
|
-
let r1 =
|
|
90
|
-
let _r2 =
|
|
78
|
+
let r1 = worker.poll_activity_task().await.unwrap();
|
|
79
|
+
let _r2 = worker.poll_activity_task().await.unwrap();
|
|
91
80
|
// Third should block until we complete one of the first two
|
|
92
81
|
let last_finisher = AtomicUsize::new(0);
|
|
93
82
|
tokio::join! {
|
|
94
83
|
async {
|
|
95
|
-
|
|
84
|
+
worker.complete_activity_task(ActivityTaskCompletion {
|
|
96
85
|
task_token: r1.task_token,
|
|
97
|
-
task_queue: TEST_Q.to_string(),
|
|
98
86
|
result: Some(ActivityExecutionResult::ok(vec![1].into()))
|
|
99
87
|
}).await.unwrap();
|
|
100
88
|
last_finisher.store(1, Ordering::SeqCst);
|
|
101
89
|
},
|
|
102
90
|
async {
|
|
103
|
-
|
|
91
|
+
worker.poll_activity_task().await.unwrap();
|
|
104
92
|
last_finisher.store(2, Ordering::SeqCst);
|
|
105
93
|
}
|
|
106
94
|
};
|
|
@@ -110,19 +98,14 @@ async fn max_activities_respected() {
|
|
|
110
98
|
|
|
111
99
|
#[tokio::test]
|
|
112
100
|
async fn activity_not_found_returns_ok() {
|
|
113
|
-
let mut
|
|
101
|
+
let mut mock_client = mock_workflow_client();
|
|
114
102
|
// Mock won't even be called, since we weren't tracking activity
|
|
115
|
-
|
|
103
|
+
mock_client.expect_complete_activity_task().times(0);
|
|
116
104
|
|
|
117
|
-
let core =
|
|
118
|
-
mock_gateway,
|
|
119
|
-
[],
|
|
120
|
-
[],
|
|
121
|
-
));
|
|
105
|
+
let core = mock_worker(MocksHolder::from_client_with_responses(mock_client, [], []));
|
|
122
106
|
|
|
123
107
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
124
108
|
task_token: vec![1],
|
|
125
|
-
task_queue: TEST_Q.to_string(),
|
|
126
109
|
result: Some(ActivityExecutionResult::ok(vec![1].into())),
|
|
127
110
|
})
|
|
128
111
|
.await
|
|
@@ -132,8 +115,8 @@ async fn activity_not_found_returns_ok() {
|
|
|
132
115
|
|
|
133
116
|
#[tokio::test]
|
|
134
117
|
async fn heartbeats_report_cancels_only_once() {
|
|
135
|
-
let mut
|
|
136
|
-
|
|
118
|
+
let mut mock_client = mock_workflow_client();
|
|
119
|
+
mock_client
|
|
137
120
|
.expect_record_activity_heartbeat()
|
|
138
121
|
.times(2)
|
|
139
122
|
.returning(|_, _| {
|
|
@@ -141,17 +124,17 @@ async fn heartbeats_report_cancels_only_once() {
|
|
|
141
124
|
cancel_requested: true,
|
|
142
125
|
})
|
|
143
126
|
});
|
|
144
|
-
|
|
127
|
+
mock_client
|
|
145
128
|
.expect_complete_activity_task()
|
|
146
129
|
.times(1)
|
|
147
130
|
.returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
|
|
148
|
-
|
|
131
|
+
mock_client
|
|
149
132
|
.expect_cancel_activity_task()
|
|
150
133
|
.times(1)
|
|
151
134
|
.returning(|_, _| Ok(RespondActivityTaskCanceledResponse::default()));
|
|
152
135
|
|
|
153
|
-
let core =
|
|
154
|
-
|
|
136
|
+
let core = mock_worker(MocksHolder::from_client_with_responses(
|
|
137
|
+
mock_client,
|
|
155
138
|
[],
|
|
156
139
|
[
|
|
157
140
|
PollActivityTaskQueueResponse {
|
|
@@ -169,15 +152,14 @@ async fn heartbeats_report_cancels_only_once() {
|
|
|
169
152
|
],
|
|
170
153
|
));
|
|
171
154
|
|
|
172
|
-
let act = core.poll_activity_task(
|
|
155
|
+
let act = core.poll_activity_task().await.unwrap();
|
|
173
156
|
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
174
157
|
task_token: act.task_token.clone(),
|
|
175
|
-
task_queue: TEST_Q.to_string(),
|
|
176
158
|
details: vec![vec![1_u8, 2, 3].into()],
|
|
177
159
|
});
|
|
178
160
|
// We have to wait a beat for the heartbeat to be processed
|
|
179
161
|
sleep(Duration::from_millis(10)).await;
|
|
180
|
-
let act = core.poll_activity_task(
|
|
162
|
+
let act = core.poll_activity_task().await.unwrap();
|
|
181
163
|
assert_matches!(
|
|
182
164
|
&act,
|
|
183
165
|
ActivityTask {
|
|
@@ -192,7 +174,6 @@ async fn heartbeats_report_cancels_only_once() {
|
|
|
192
174
|
sleep(Duration::from_millis(10)).await;
|
|
193
175
|
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
194
176
|
task_token: act.task_token.clone(),
|
|
195
|
-
task_queue: TEST_Q.to_string(),
|
|
196
177
|
details: vec![vec![1_u8, 2, 3].into()],
|
|
197
178
|
});
|
|
198
179
|
// Wait delay again to flush heartbeat
|
|
@@ -200,14 +181,14 @@ async fn heartbeats_report_cancels_only_once() {
|
|
|
200
181
|
// Now complete it as cancelled
|
|
201
182
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
202
183
|
task_token: act.task_token,
|
|
203
|
-
|
|
184
|
+
|
|
204
185
|
result: Some(ActivityExecutionResult::cancel_from_details(None)),
|
|
205
186
|
})
|
|
206
187
|
.await
|
|
207
188
|
.unwrap();
|
|
208
189
|
// Since cancels always come before new tasks, if we get a new non-cancel task, we did not
|
|
209
190
|
// double-issue cancels.
|
|
210
|
-
let act = core.poll_activity_task(
|
|
191
|
+
let act = core.poll_activity_task().await.unwrap();
|
|
211
192
|
assert_matches!(
|
|
212
193
|
&act,
|
|
213
194
|
ActivityTask {
|
|
@@ -219,7 +200,7 @@ async fn heartbeats_report_cancels_only_once() {
|
|
|
219
200
|
// Complete it so shutdown goes through
|
|
220
201
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
221
202
|
task_token: act.task_token,
|
|
222
|
-
|
|
203
|
+
|
|
223
204
|
result: Some(ActivityExecutionResult::ok(vec![1].into())),
|
|
224
205
|
})
|
|
225
206
|
.await
|
|
@@ -250,8 +231,8 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
250
231
|
.times(2)
|
|
251
232
|
.returning(move || poll_resps.pop_front().unwrap());
|
|
252
233
|
|
|
253
|
-
let mut
|
|
254
|
-
|
|
234
|
+
let mut mock_client = mock_manual_workflow_client();
|
|
235
|
+
mock_client
|
|
255
236
|
.expect_record_activity_heartbeat()
|
|
256
237
|
.times(1)
|
|
257
238
|
.returning(|_, _| {
|
|
@@ -262,37 +243,36 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
262
243
|
}
|
|
263
244
|
.boxed()
|
|
264
245
|
});
|
|
265
|
-
|
|
246
|
+
mock_client
|
|
266
247
|
.expect_complete_activity_task()
|
|
267
248
|
.times(1)
|
|
268
249
|
.returning(|_, _| async { Ok(RespondActivityTaskCompletedResponse::default()) }.boxed());
|
|
269
250
|
|
|
270
|
-
let
|
|
251
|
+
let mw = MockWorker {
|
|
271
252
|
act_poller: Some(Box::from(mock_poller)),
|
|
272
253
|
..Default::default()
|
|
273
254
|
};
|
|
274
|
-
|
|
275
|
-
let core = mock_core(MocksHolder::from_mock_workers(mock_gateway, [mock_worker]));
|
|
255
|
+
let core = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
|
|
276
256
|
let last_finisher = AtomicUsize::new(0);
|
|
277
257
|
// Perform first poll to get the activity registered
|
|
278
|
-
let act = core.poll_activity_task(
|
|
258
|
+
let act = core.poll_activity_task().await.unwrap();
|
|
279
259
|
// Poll should block until heartbeat is sent, issuing the cancel, and interrupting the poll
|
|
280
260
|
tokio::join! {
|
|
281
261
|
async {
|
|
282
262
|
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
283
263
|
task_token: act.task_token,
|
|
284
|
-
|
|
264
|
+
|
|
285
265
|
details: vec![vec![1_u8, 2, 3].into()],
|
|
286
266
|
});
|
|
287
267
|
last_finisher.store(1, Ordering::SeqCst);
|
|
288
268
|
},
|
|
289
269
|
async {
|
|
290
|
-
let act = core.poll_activity_task(
|
|
270
|
+
let act = core.poll_activity_task().await.unwrap();
|
|
291
271
|
// Must complete this activity for shutdown to finish
|
|
292
272
|
core.complete_activity_task(
|
|
293
273
|
ActivityTaskCompletion {
|
|
294
274
|
task_token: act.task_token,
|
|
295
|
-
|
|
275
|
+
|
|
296
276
|
result: Some(ActivityExecutionResult::ok(vec![1].into())),
|
|
297
277
|
}
|
|
298
278
|
).await.unwrap();
|
|
@@ -306,7 +286,7 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
306
286
|
|
|
307
287
|
#[tokio::test]
|
|
308
288
|
async fn activity_poll_timeout_retries() {
|
|
309
|
-
let
|
|
289
|
+
let mock_client = mock_workflow_client();
|
|
310
290
|
let mut calls = 0;
|
|
311
291
|
let mut mock_act_poller = mock_poller();
|
|
312
292
|
mock_act_poller.expect_poll().times(3).returning(move || {
|
|
@@ -320,12 +300,12 @@ async fn activity_poll_timeout_retries() {
|
|
|
320
300
|
}))
|
|
321
301
|
}
|
|
322
302
|
});
|
|
323
|
-
let
|
|
303
|
+
let mw = MockWorker {
|
|
324
304
|
act_poller: Some(Box::from(mock_act_poller)),
|
|
325
305
|
..Default::default()
|
|
326
306
|
};
|
|
327
|
-
let core =
|
|
328
|
-
let r = core.poll_activity_task(
|
|
307
|
+
let core = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
|
|
308
|
+
let r = core.poll_activity_task().await.unwrap();
|
|
329
309
|
assert_matches!(r.task_token.as_slice(), b"hello!");
|
|
330
310
|
}
|
|
331
311
|
|
|
@@ -335,7 +315,7 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
335
315
|
// them after a few successful heartbeats
|
|
336
316
|
const CONCURRENCY_NUM: usize = 5;
|
|
337
317
|
|
|
338
|
-
let mut
|
|
318
|
+
let mut mock_client = mock_manual_workflow_client();
|
|
339
319
|
let mut poll_resps = VecDeque::from(
|
|
340
320
|
(0..CONCURRENCY_NUM)
|
|
341
321
|
.map(|i| {
|
|
@@ -361,13 +341,13 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
361
341
|
.boxed(),
|
|
362
342
|
);
|
|
363
343
|
let mut calls_map = HashMap::<_, i32>::new();
|
|
364
|
-
|
|
344
|
+
mock_client
|
|
365
345
|
.expect_poll_activity_task()
|
|
366
|
-
.returning(move |_| poll_resps.pop_front().unwrap());
|
|
367
|
-
|
|
346
|
+
.returning(move |_, _| poll_resps.pop_front().unwrap());
|
|
347
|
+
mock_client
|
|
368
348
|
.expect_cancel_activity_task()
|
|
369
349
|
.returning(move |_, _| async move { Ok(Default::default()) }.boxed());
|
|
370
|
-
|
|
350
|
+
mock_client
|
|
371
351
|
.expect_record_activity_heartbeat()
|
|
372
352
|
.returning(move |tt, _| {
|
|
373
353
|
let calls = match calls_map.entry(tt) {
|
|
@@ -391,30 +371,27 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
391
371
|
.boxed()
|
|
392
372
|
});
|
|
393
373
|
|
|
394
|
-
let
|
|
395
|
-
|
|
396
|
-
WorkerConfigBuilder::default()
|
|
397
|
-
.task_queue(TEST_Q)
|
|
374
|
+
let worker = &Worker::new_test(
|
|
375
|
+
test_worker_cfg()
|
|
398
376
|
.max_outstanding_activities(CONCURRENCY_NUM)
|
|
399
377
|
// Only 1 poll at a time to avoid over-polling and running out of responses
|
|
400
378
|
.max_concurrent_at_polls(1_usize)
|
|
401
379
|
.build()
|
|
402
380
|
.unwrap(),
|
|
403
|
-
|
|
404
|
-
|
|
381
|
+
mock_client,
|
|
382
|
+
);
|
|
405
383
|
|
|
406
384
|
// Poll all activities first so they are registered
|
|
407
385
|
for _ in 0..CONCURRENCY_NUM {
|
|
408
|
-
|
|
386
|
+
worker.poll_activity_task().await.unwrap();
|
|
409
387
|
}
|
|
410
388
|
|
|
411
389
|
// Spawn "activities"
|
|
412
390
|
fanout_tasks(CONCURRENCY_NUM, |i| async move {
|
|
413
391
|
let task_token = i.to_be_bytes().to_vec();
|
|
414
392
|
for _ in 0..12 {
|
|
415
|
-
|
|
393
|
+
worker.record_activity_heartbeat(ActivityHeartbeat {
|
|
416
394
|
task_token: task_token.clone(),
|
|
417
|
-
task_queue: TEST_Q.to_string(),
|
|
418
395
|
details: vec![],
|
|
419
396
|
});
|
|
420
397
|
sleep(Duration::from_millis(50)).await;
|
|
@@ -424,7 +401,7 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
424
401
|
|
|
425
402
|
// Read all the cancellations and reply to them concurrently
|
|
426
403
|
fanout_tasks(CONCURRENCY_NUM, |_| async move {
|
|
427
|
-
let r =
|
|
404
|
+
let r = worker.poll_activity_task().await.unwrap();
|
|
428
405
|
assert_matches!(
|
|
429
406
|
r,
|
|
430
407
|
ActivityTask {
|
|
@@ -432,23 +409,23 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
432
409
|
..
|
|
433
410
|
}
|
|
434
411
|
);
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
412
|
+
worker
|
|
413
|
+
.complete_activity_task(ActivityTaskCompletion {
|
|
414
|
+
task_token: r.task_token.clone(),
|
|
415
|
+
result: Some(ActivityExecutionResult::cancel_from_details(None)),
|
|
416
|
+
})
|
|
417
|
+
.await
|
|
418
|
+
.unwrap();
|
|
442
419
|
})
|
|
443
420
|
.await;
|
|
444
421
|
|
|
445
|
-
|
|
422
|
+
worker.shutdown().await;
|
|
446
423
|
}
|
|
447
424
|
|
|
448
425
|
#[tokio::test]
|
|
449
426
|
async fn activity_timeout_no_double_resolve() {
|
|
450
427
|
let t = canned_histories::activity_double_resolve_repro();
|
|
451
|
-
let core =
|
|
428
|
+
let core = build_fake_worker("fake_wf_id", t, &[3]);
|
|
452
429
|
let activity_id = 1;
|
|
453
430
|
|
|
454
431
|
poll_and_reply(
|
|
@@ -497,84 +474,10 @@ async fn activity_timeout_no_double_resolve() {
|
|
|
497
474
|
core.shutdown().await;
|
|
498
475
|
}
|
|
499
476
|
|
|
500
|
-
#[tokio::test]
|
|
501
|
-
async fn only_returns_cancels_for_desired_queue() {
|
|
502
|
-
let mut mock_gateway = mock_gateway();
|
|
503
|
-
let seen_cancel = Arc::new(Notify::new());
|
|
504
|
-
let sc = seen_cancel.clone();
|
|
505
|
-
mock_gateway
|
|
506
|
-
.expect_record_activity_heartbeat()
|
|
507
|
-
.times(1)
|
|
508
|
-
.returning(move |_, _| {
|
|
509
|
-
// Mark the activity as needing cancel when heartbeated
|
|
510
|
-
sc.notify_one();
|
|
511
|
-
Ok(RecordActivityTaskHeartbeatResponse {
|
|
512
|
-
cancel_requested: true,
|
|
513
|
-
})
|
|
514
|
-
});
|
|
515
|
-
mock_gateway
|
|
516
|
-
.expect_cancel_activity_task()
|
|
517
|
-
.times(1)
|
|
518
|
-
.returning(|_, _| Ok(RespondActivityTaskCanceledResponse::default()));
|
|
519
|
-
|
|
520
|
-
let mut w1 = MockWorker::for_queue("q1");
|
|
521
|
-
w1.act_poller = Some(mock_poller_from_resps([PollActivityTaskQueueResponse {
|
|
522
|
-
task_token: vec![1],
|
|
523
|
-
activity_id: "act1".to_string(),
|
|
524
|
-
heartbeat_timeout: Some(Duration::from_millis(1).into()),
|
|
525
|
-
..Default::default()
|
|
526
|
-
}]));
|
|
527
|
-
let mut mock_act_poller = mock_poller();
|
|
528
|
-
mock_act_poller
|
|
529
|
-
.expect_poll()
|
|
530
|
-
.returning(|| Some(Ok(Default::default())));
|
|
531
|
-
let mut w2 = MockWorker::for_queue("q2");
|
|
532
|
-
w2.act_poller = Some(Box::from(mock_act_poller));
|
|
533
|
-
|
|
534
|
-
let core = mock_core(MocksHolder::from_mock_workers(mock_gateway, [w1, w2]));
|
|
535
|
-
// First poll should get the activity
|
|
536
|
-
core.poll_activity_task("q1").await.unwrap();
|
|
537
|
-
// Now record a heartbeat which will get the cancel response and mark the act as cancelled
|
|
538
|
-
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
539
|
-
task_token: vec![1],
|
|
540
|
-
task_queue: "q1".to_string(),
|
|
541
|
-
details: vec![],
|
|
542
|
-
});
|
|
543
|
-
// Worker two's poll should never resolve, since it's just getting pretend long poll timeouts
|
|
544
|
-
let q1poll = async {
|
|
545
|
-
// Wait for cancel to propagate
|
|
546
|
-
seen_cancel.notified().await;
|
|
547
|
-
core.poll_activity_task("q1").await
|
|
548
|
-
};
|
|
549
|
-
let q1_res = tokio::select! {
|
|
550
|
-
_ = core.poll_activity_task("q2") => panic!("q2 poll resolved!"),
|
|
551
|
-
r = q1poll => {
|
|
552
|
-
r.unwrap()
|
|
553
|
-
}
|
|
554
|
-
};
|
|
555
|
-
assert_matches!(
|
|
556
|
-
&q1_res,
|
|
557
|
-
ActivityTask {
|
|
558
|
-
variant: Some(activity_task::Variant::Cancel(_)),
|
|
559
|
-
..
|
|
560
|
-
}
|
|
561
|
-
);
|
|
562
|
-
// act needs to complete to finalize shutdown
|
|
563
|
-
core.complete_activity_task(ActivityTaskCompletion {
|
|
564
|
-
task_token: q1_res.task_token,
|
|
565
|
-
task_queue: "q1".to_string(),
|
|
566
|
-
result: Some(ActivityExecutionResult::cancel_from_details(None)),
|
|
567
|
-
})
|
|
568
|
-
.await
|
|
569
|
-
.unwrap();
|
|
570
|
-
|
|
571
|
-
core.shutdown().await;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
477
|
#[tokio::test]
|
|
575
478
|
async fn can_heartbeat_acts_during_shutdown() {
|
|
576
|
-
let mut
|
|
577
|
-
|
|
479
|
+
let mut mock_client = mock_workflow_client();
|
|
480
|
+
mock_client
|
|
578
481
|
.expect_record_activity_heartbeat()
|
|
579
482
|
.times(1)
|
|
580
483
|
.returning(|_, _| {
|
|
@@ -582,13 +485,13 @@ async fn can_heartbeat_acts_during_shutdown() {
|
|
|
582
485
|
cancel_requested: false,
|
|
583
486
|
})
|
|
584
487
|
});
|
|
585
|
-
|
|
488
|
+
mock_client
|
|
586
489
|
.expect_complete_activity_task()
|
|
587
490
|
.times(1)
|
|
588
491
|
.returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
|
|
589
492
|
|
|
590
|
-
let core =
|
|
591
|
-
|
|
493
|
+
let core = mock_worker(MocksHolder::from_client_with_responses(
|
|
494
|
+
mock_client,
|
|
592
495
|
[],
|
|
593
496
|
[PollActivityTaskQueueResponse {
|
|
594
497
|
task_token: vec![1],
|
|
@@ -598,7 +501,7 @@ async fn can_heartbeat_acts_during_shutdown() {
|
|
|
598
501
|
}],
|
|
599
502
|
));
|
|
600
503
|
|
|
601
|
-
let act = core.poll_activity_task(
|
|
504
|
+
let act = core.poll_activity_task().await.unwrap();
|
|
602
505
|
let complete_order = RefCell::new(vec![]);
|
|
603
506
|
// Start shutdown before completing the activity
|
|
604
507
|
let shutdown_fut = async {
|
|
@@ -608,12 +511,12 @@ async fn can_heartbeat_acts_during_shutdown() {
|
|
|
608
511
|
let complete_fut = async {
|
|
609
512
|
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
610
513
|
task_token: act.task_token.clone(),
|
|
611
|
-
|
|
514
|
+
|
|
612
515
|
details: vec![vec![1_u8, 2, 3].into()],
|
|
613
516
|
});
|
|
614
517
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
615
518
|
task_token: act.task_token,
|
|
616
|
-
|
|
519
|
+
|
|
617
520
|
result: Some(ActivityExecutionResult::ok(vec![1].into())),
|
|
618
521
|
})
|
|
619
522
|
.await
|
|
@@ -629,10 +532,10 @@ async fn can_heartbeat_acts_during_shutdown() {
|
|
|
629
532
|
#[tokio::test]
|
|
630
533
|
async fn complete_act_with_fail_flushes_heartbeat() {
|
|
631
534
|
let last_hb = 50;
|
|
632
|
-
let mut
|
|
535
|
+
let mut mock_client = mock_workflow_client();
|
|
633
536
|
let last_seen_payload = Rc::new(RefCell::new(None));
|
|
634
537
|
let lsp = last_seen_payload.clone();
|
|
635
|
-
|
|
538
|
+
mock_client
|
|
636
539
|
.expect_record_activity_heartbeat()
|
|
637
540
|
// Two times b/c we always record the first heartbeat, and we'll flush the last
|
|
638
541
|
.times(2)
|
|
@@ -642,13 +545,13 @@ async fn complete_act_with_fail_flushes_heartbeat() {
|
|
|
642
545
|
cancel_requested: false,
|
|
643
546
|
})
|
|
644
547
|
});
|
|
645
|
-
|
|
548
|
+
mock_client
|
|
646
549
|
.expect_fail_activity_task()
|
|
647
550
|
.times(1)
|
|
648
551
|
.returning(|_, _| Ok(RespondActivityTaskFailedResponse::default()));
|
|
649
552
|
|
|
650
|
-
let core =
|
|
651
|
-
|
|
553
|
+
let core = mock_worker(MocksHolder::from_client_with_responses(
|
|
554
|
+
mock_client,
|
|
652
555
|
[],
|
|
653
556
|
[PollActivityTaskQueueResponse {
|
|
654
557
|
task_token: vec![1],
|
|
@@ -658,18 +561,16 @@ async fn complete_act_with_fail_flushes_heartbeat() {
|
|
|
658
561
|
}],
|
|
659
562
|
));
|
|
660
563
|
|
|
661
|
-
let act = core.poll_activity_task(
|
|
564
|
+
let act = core.poll_activity_task().await.unwrap();
|
|
662
565
|
// Record a bunch of heartbeats
|
|
663
566
|
for i in 1..=last_hb {
|
|
664
567
|
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
665
568
|
task_token: act.task_token.clone(),
|
|
666
|
-
task_queue: TEST_Q.to_string(),
|
|
667
569
|
details: vec![vec![i].into()],
|
|
668
570
|
});
|
|
669
571
|
}
|
|
670
572
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
671
573
|
task_token: act.task_token.clone(),
|
|
672
|
-
task_queue: TEST_Q.to_string(),
|
|
673
574
|
result: Some(ActivityExecutionResult::fail("Ahh".into())),
|
|
674
575
|
})
|
|
675
576
|
.await
|
|
@@ -680,3 +581,28 @@ async fn complete_act_with_fail_flushes_heartbeat() {
|
|
|
680
581
|
let last_seen_payload = &last_seen_payload.take().unwrap().payloads[0];
|
|
681
582
|
assert_eq!(last_seen_payload.data, &[last_hb]);
|
|
682
583
|
}
|
|
584
|
+
|
|
585
|
+
#[tokio::test]
|
|
586
|
+
async fn max_tq_acts_set_passed_to_poll_properly() {
|
|
587
|
+
let rate = 9.28;
|
|
588
|
+
let mut mock_client = mock_workflow_client();
|
|
589
|
+
mock_client
|
|
590
|
+
.expect_poll_activity_task()
|
|
591
|
+
.returning(move |_, tps| {
|
|
592
|
+
assert_eq!(tps, Some(rate));
|
|
593
|
+
Ok(PollActivityTaskQueueResponse {
|
|
594
|
+
task_token: vec![1],
|
|
595
|
+
..Default::default()
|
|
596
|
+
})
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
let cfg = WorkerConfigBuilder::default()
|
|
600
|
+
.namespace("enchi")
|
|
601
|
+
.task_queue("cat")
|
|
602
|
+
.max_concurrent_at_polls(1_usize)
|
|
603
|
+
.max_task_queue_activities_per_second(rate)
|
|
604
|
+
.build()
|
|
605
|
+
.unwrap();
|
|
606
|
+
let worker = Worker::new_test(cfg, mock_client);
|
|
607
|
+
worker.poll_activity_task().await.unwrap();
|
|
608
|
+
}
|