@temporalio/core-bridge 0.23.0 → 1.0.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 +118 -15
- package/Cargo.toml +2 -1
- package/LICENSE.md +1 -1
- package/README.md +1 -1
- package/index.d.ts +47 -18
- package/package.json +7 -7
- 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/docker-compose.yaml +4 -2
- package/sdk-core/ARCHITECTURE.md +9 -7
- package/sdk-core/README.md +5 -1
- package/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
- package/sdk-core/bridge-ffi/src/wrappers.rs +0 -3
- package/sdk-core/client/src/lib.rs +26 -8
- package/sdk-core/client/src/raw.rs +166 -54
- package/sdk-core/client/src/retry.rs +9 -4
- package/sdk-core/client/src/workflow_handle/mod.rs +4 -2
- package/sdk-core/core/Cargo.toml +2 -0
- package/sdk-core/core/src/abstractions.rs +137 -16
- package/sdk-core/core/src/core_tests/activity_tasks.rs +258 -63
- package/sdk-core/core/src/core_tests/child_workflows.rs +1 -2
- package/sdk-core/core/src/core_tests/determinism.rs +2 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +8 -7
- package/sdk-core/core/src/core_tests/queries.rs +146 -60
- package/sdk-core/core/src/core_tests/replay_flag.rs +1 -1
- package/sdk-core/core/src/core_tests/workers.rs +39 -23
- package/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +387 -280
- package/sdk-core/core/src/lib.rs +6 -4
- package/sdk-core/core/src/pollers/poll_buffer.rs +16 -10
- package/sdk-core/core/src/protosext/mod.rs +6 -6
- package/sdk-core/core/src/retry_logic.rs +1 -1
- package/sdk-core/core/src/telemetry/metrics.rs +21 -7
- package/sdk-core/core/src/telemetry/mod.rs +18 -4
- package/sdk-core/core/src/test_help/mod.rs +341 -109
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +18 -9
- package/sdk-core/core/src/worker/activities/local_activities.rs +19 -16
- package/sdk-core/core/src/worker/activities.rs +156 -29
- package/sdk-core/core/src/worker/client.rs +1 -0
- package/sdk-core/core/src/worker/mod.rs +132 -659
- package/sdk-core/core/src/{workflow → worker/workflow}/bridge.rs +1 -1
- package/sdk-core/core/src/{workflow → worker/workflow}/driven_workflow.rs +1 -1
- package/sdk-core/core/src/{workflow → worker/workflow}/history_update.rs +16 -2
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/activity_state_machine.rs +39 -4
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/cancel_external_state_machine.rs +5 -2
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/cancel_workflow_state_machine.rs +1 -1
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/child_workflow_state_machine.rs +2 -4
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/complete_workflow_state_machine.rs +0 -0
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/continue_as_new_workflow_state_machine.rs +1 -1
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/fail_workflow_state_machine.rs +0 -0
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/local_activity_state_machine.rs +2 -5
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/mod.rs +1 -1
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/patch_state_machine.rs +1 -1
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/side_effect_state_machine.rs +0 -0
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/signal_external_state_machine.rs +4 -2
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/timer_state_machine.rs +1 -2
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/transition_coverage.rs +1 -1
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/upsert_search_attributes_state_machine.rs +5 -7
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_machines/local_acts.rs +2 -2
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_machines.rs +40 -16
- package/sdk-core/core/src/{workflow → worker/workflow}/machines/workflow_task_state_machine.rs +0 -0
- package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
- package/sdk-core/core/src/worker/workflow/managed_run.rs +627 -0
- package/sdk-core/core/src/worker/workflow/mod.rs +1115 -0
- package/sdk-core/core/src/worker/workflow/run_cache.rs +143 -0
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +936 -0
- package/sdk-core/core-api/src/errors.rs +3 -10
- package/sdk-core/core-api/src/lib.rs +2 -1
- package/sdk-core/core-api/src/worker.rs +26 -2
- package/sdk-core/etc/dynamic-config.yaml +2 -0
- package/sdk-core/integ-with-otel.sh +1 -1
- package/sdk-core/protos/api_upstream/Makefile +4 -4
- package/sdk-core/protos/api_upstream/api-linter.yaml +2 -0
- package/sdk-core/protos/api_upstream/buf.yaml +8 -9
- package/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +83 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +40 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +3 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +32 -4
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +69 -19
- package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +13 -0
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +163 -0
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +97 -0
- package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +300 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +25 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +180 -3
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +53 -3
- package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +2 -2
- package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +6 -5
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -1
- package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +2 -1
- package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +0 -64
- package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -1
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +11 -8
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +30 -25
- package/sdk-core/sdk/src/activity_context.rs +12 -5
- package/sdk-core/sdk/src/app_data.rs +37 -0
- package/sdk-core/sdk/src/lib.rs +76 -43
- package/sdk-core/sdk/src/workflow_context/options.rs +8 -6
- package/sdk-core/sdk/src/workflow_context.rs +14 -19
- package/sdk-core/sdk/src/workflow_future.rs +11 -6
- package/sdk-core/sdk-core-protos/src/history_builder.rs +19 -5
- package/sdk-core/sdk-core-protos/src/history_info.rs +11 -6
- package/sdk-core/sdk-core-protos/src/lib.rs +74 -176
- package/sdk-core/test-utils/src/lib.rs +85 -72
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -9
- package/sdk-core/tests/integ_tests/polling_tests.rs +12 -0
- package/sdk-core/tests/integ_tests/queries_tests.rs +39 -22
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +49 -4
- package/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +74 -13
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +19 -0
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -3
- package/sdk-core/tests/integ_tests/workflow_tests.rs +10 -23
- package/sdk-core/tests/load_tests.rs +8 -3
- package/sdk-core/tests/main.rs +2 -1
- package/src/conversions.rs +47 -39
- package/src/errors.rs +10 -21
- package/src/lib.rs +342 -325
- package/sdk-core/core/src/pending_activations.rs +0 -173
- package/sdk-core/core/src/worker/wft_delivery.rs +0 -81
- package/sdk-core/core/src/workflow/mod.rs +0 -478
- package/sdk-core/core/src/workflow/workflow_tasks/cache_manager.rs +0 -194
- package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +0 -418
- package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +0 -989
|
@@ -2,22 +2,31 @@ pub(crate) use temporal_sdk_core_test_utils::canned_histories;
|
|
|
2
2
|
|
|
3
3
|
use crate::{
|
|
4
4
|
pollers::{BoxedActPoller, BoxedPoller, BoxedWFPoller, MockManualPoller, MockPoller},
|
|
5
|
+
protosext::ValidPollWFTQResponse,
|
|
5
6
|
replay::TestHistoryBuilder,
|
|
6
7
|
sticky_q_name_for_worker,
|
|
7
|
-
worker::
|
|
8
|
-
|
|
8
|
+
worker::{
|
|
9
|
+
client::{mocks::mock_workflow_client, MockWorkerClient, WorkerClient},
|
|
10
|
+
new_wft_poller,
|
|
11
|
+
},
|
|
9
12
|
TaskToken, Worker, WorkerClientBag, WorkerConfig, WorkerConfigBuilder,
|
|
10
13
|
};
|
|
11
14
|
use bimap::BiMap;
|
|
12
|
-
use futures::FutureExt;
|
|
15
|
+
use futures::{future::BoxFuture, stream, stream::BoxStream, FutureExt, Stream, StreamExt};
|
|
13
16
|
use mockall::TimesRange;
|
|
14
17
|
use parking_lot::RwLock;
|
|
15
18
|
use std::{
|
|
16
19
|
collections::{BTreeMap, HashMap, HashSet, VecDeque},
|
|
17
|
-
ops::
|
|
18
|
-
|
|
20
|
+
ops::{Deref, DerefMut},
|
|
21
|
+
pin::Pin,
|
|
22
|
+
sync::{
|
|
23
|
+
atomic::{AtomicBool, Ordering},
|
|
24
|
+
Arc,
|
|
25
|
+
},
|
|
26
|
+
task::{Context, Poll},
|
|
19
27
|
time::Duration,
|
|
20
28
|
};
|
|
29
|
+
use temporal_client::WorkflowTaskCompletion;
|
|
21
30
|
use temporal_sdk_core_api::Worker as WorkerTrait;
|
|
22
31
|
use temporal_sdk_core_protos::{
|
|
23
32
|
coresdk::{
|
|
@@ -36,39 +45,61 @@ use temporal_sdk_core_protos::{
|
|
|
36
45
|
},
|
|
37
46
|
};
|
|
38
47
|
use temporal_sdk_core_test_utils::TestWorker;
|
|
39
|
-
use tokio::sync::Notify;
|
|
48
|
+
use tokio::sync::{mpsc::unbounded_channel, Notify};
|
|
49
|
+
use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
50
|
+
use tokio_util::sync::CancellationToken;
|
|
40
51
|
|
|
41
52
|
pub const TEST_Q: &str = "q";
|
|
42
53
|
pub static NO_MORE_WORK_ERROR_MSG: &str = "No more work to do";
|
|
43
54
|
|
|
44
55
|
pub fn test_worker_cfg() -> WorkerConfigBuilder {
|
|
45
56
|
let mut wcb = WorkerConfigBuilder::default();
|
|
46
|
-
wcb.namespace("default")
|
|
57
|
+
wcb.namespace("default")
|
|
58
|
+
.task_queue(TEST_Q)
|
|
59
|
+
.worker_build_id("test_bin_id")
|
|
60
|
+
// Serial polling since it makes mocking much easier.
|
|
61
|
+
.max_concurrent_wft_polls(1_usize);
|
|
47
62
|
wcb
|
|
48
63
|
}
|
|
49
64
|
|
|
50
65
|
/// When constructing responses for mocks, indicates how a given response should be built
|
|
51
|
-
#[derive(derive_more::From
|
|
66
|
+
#[derive(derive_more::From)]
|
|
67
|
+
#[allow(clippy::large_enum_variant)] // Test only code, whatever.
|
|
52
68
|
pub enum ResponseType {
|
|
53
69
|
ToTaskNum(usize),
|
|
54
70
|
/// Returns just the history after the WFT completed of the provided task number - 1, through to
|
|
55
71
|
/// the next WFT started. Simulating the incremental history for just the provided task number
|
|
56
72
|
#[from(ignore)]
|
|
57
73
|
OneTask(usize),
|
|
74
|
+
/// Waits until the future resolves before responding as `ToTaskNum` with the provided number
|
|
75
|
+
UntilResolved(BoxFuture<'static, ()>, usize),
|
|
58
76
|
AllHistory,
|
|
77
|
+
Raw(PollWorkflowTaskQueueResponse),
|
|
78
|
+
}
|
|
79
|
+
#[derive(Eq, PartialEq, Hash)]
|
|
80
|
+
pub enum HashableResponseType {
|
|
81
|
+
ToTaskNum(usize),
|
|
82
|
+
OneTask(usize),
|
|
83
|
+
UntilResolved(usize),
|
|
84
|
+
AllHistory,
|
|
85
|
+
Raw(TaskToken),
|
|
86
|
+
}
|
|
87
|
+
impl ResponseType {
|
|
88
|
+
pub fn hashable(&self) -> HashableResponseType {
|
|
89
|
+
match self {
|
|
90
|
+
ResponseType::ToTaskNum(x) => HashableResponseType::ToTaskNum(*x),
|
|
91
|
+
ResponseType::OneTask(x) => HashableResponseType::OneTask(*x),
|
|
92
|
+
ResponseType::AllHistory => HashableResponseType::AllHistory,
|
|
93
|
+
ResponseType::Raw(r) => HashableResponseType::Raw(r.task_token.clone().into()),
|
|
94
|
+
ResponseType::UntilResolved(_, x) => HashableResponseType::UntilResolved(*x),
|
|
95
|
+
}
|
|
96
|
+
}
|
|
59
97
|
}
|
|
60
|
-
|
|
61
98
|
impl From<&usize> for ResponseType {
|
|
62
99
|
fn from(u: &usize) -> Self {
|
|
63
100
|
Self::ToTaskNum(*u)
|
|
64
101
|
}
|
|
65
102
|
}
|
|
66
|
-
// :shrug:
|
|
67
|
-
impl From<&Self> for ResponseType {
|
|
68
|
-
fn from(r: &Self) -> Self {
|
|
69
|
-
*r
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
103
|
|
|
73
104
|
/// Given identifiers for a workflow/run, and a test history builder, construct an instance of
|
|
74
105
|
/// the a worker with a mock server client that will produce the responses as appropriate.
|
|
@@ -89,20 +120,21 @@ pub(crate) fn build_fake_worker(
|
|
|
89
120
|
response_batches,
|
|
90
121
|
}],
|
|
91
122
|
true,
|
|
92
|
-
|
|
123
|
+
0,
|
|
93
124
|
);
|
|
94
125
|
mock_worker(mock_holder)
|
|
95
126
|
}
|
|
96
127
|
|
|
97
128
|
pub(crate) fn mock_worker(mocks: MocksHolder) -> Worker {
|
|
98
|
-
let sticky_q = sticky_q_name_for_worker("unit-test", &mocks.
|
|
129
|
+
let sticky_q = sticky_q_name_for_worker("unit-test", &mocks.inputs.config);
|
|
99
130
|
Worker::new_with_pollers(
|
|
100
|
-
mocks.
|
|
131
|
+
mocks.inputs.config,
|
|
101
132
|
sticky_q,
|
|
102
133
|
Arc::new(mocks.client_bag),
|
|
103
|
-
mocks.
|
|
104
|
-
mocks.
|
|
134
|
+
mocks.inputs.wft_stream,
|
|
135
|
+
mocks.inputs.act_poller,
|
|
105
136
|
Default::default(),
|
|
137
|
+
CancellationToken::new(),
|
|
106
138
|
)
|
|
107
139
|
}
|
|
108
140
|
|
|
@@ -126,35 +158,52 @@ pub struct FakeWfResponses {
|
|
|
126
158
|
pub response_batches: Vec<ResponseType>,
|
|
127
159
|
}
|
|
128
160
|
|
|
161
|
+
// TODO: Should be all-internal to this module
|
|
129
162
|
pub struct MocksHolder {
|
|
130
163
|
client_bag: WorkerClientBag,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
pub outstanding_task_map: Option<Arc<RwLock<BiMap<String, TaskToken>>>>,
|
|
164
|
+
inputs: MockWorkerInputs,
|
|
165
|
+
pub outstanding_task_map: Option<OutstandingWFTMap>,
|
|
134
166
|
}
|
|
135
167
|
|
|
136
168
|
impl MocksHolder {
|
|
137
169
|
pub fn worker_cfg(&mut self, mutator: impl FnOnce(&mut WorkerConfig)) {
|
|
138
|
-
mutator(&mut self.
|
|
170
|
+
mutator(&mut self.inputs.config);
|
|
171
|
+
}
|
|
172
|
+
pub fn set_act_poller(&mut self, poller: BoxedActPoller) {
|
|
173
|
+
self.inputs.act_poller = Some(poller);
|
|
174
|
+
}
|
|
175
|
+
/// Can be used for tests that need to avoid auto-shutdown due to running out of mock responses
|
|
176
|
+
pub fn make_wft_stream_interminable(&mut self) {
|
|
177
|
+
let old_stream = std::mem::replace(&mut self.inputs.wft_stream, stream::pending().boxed());
|
|
178
|
+
self.inputs.wft_stream = old_stream.chain(stream::pending()).boxed();
|
|
139
179
|
}
|
|
140
180
|
}
|
|
141
181
|
|
|
142
|
-
pub struct
|
|
143
|
-
pub
|
|
182
|
+
pub struct MockWorkerInputs {
|
|
183
|
+
pub wft_stream: BoxStream<'static, Result<ValidPollWFTQResponse, tonic::Status>>,
|
|
144
184
|
pub act_poller: Option<BoxedActPoller>,
|
|
145
185
|
pub config: WorkerConfig,
|
|
146
186
|
}
|
|
147
187
|
|
|
148
|
-
impl Default for
|
|
188
|
+
impl Default for MockWorkerInputs {
|
|
149
189
|
fn default() -> Self {
|
|
150
|
-
Self::
|
|
190
|
+
Self::new_from_poller(Box::from(mock_poller()))
|
|
151
191
|
}
|
|
152
192
|
}
|
|
153
193
|
|
|
154
|
-
impl
|
|
155
|
-
pub fn new(
|
|
194
|
+
impl MockWorkerInputs {
|
|
195
|
+
pub fn new(
|
|
196
|
+
wft_stream: BoxStream<'static, Result<ValidPollWFTQResponse, tonic::Status>>,
|
|
197
|
+
) -> Self {
|
|
198
|
+
Self {
|
|
199
|
+
wft_stream,
|
|
200
|
+
act_poller: None,
|
|
201
|
+
config: test_worker_cfg().build().unwrap(),
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
pub fn new_from_poller(wf_poller: BoxedWFPoller) -> Self {
|
|
156
205
|
Self {
|
|
157
|
-
wf_poller,
|
|
206
|
+
wft_stream: new_wft_poller(wf_poller, Default::default()).boxed(),
|
|
158
207
|
act_poller: None,
|
|
159
208
|
config: test_worker_cfg().build().unwrap(),
|
|
160
209
|
}
|
|
@@ -162,54 +211,83 @@ impl MockWorker {
|
|
|
162
211
|
}
|
|
163
212
|
|
|
164
213
|
impl MocksHolder {
|
|
165
|
-
pub(crate) fn from_mock_worker(
|
|
214
|
+
pub(crate) fn from_mock_worker(
|
|
215
|
+
client_bag: WorkerClientBag,
|
|
216
|
+
mock_worker: MockWorkerInputs,
|
|
217
|
+
) -> Self {
|
|
166
218
|
Self {
|
|
167
219
|
client_bag,
|
|
168
|
-
mock_worker,
|
|
220
|
+
inputs: mock_worker,
|
|
169
221
|
outstanding_task_map: None,
|
|
170
222
|
}
|
|
171
223
|
}
|
|
172
224
|
|
|
173
225
|
/// Uses the provided list of tasks to create a mock poller for the `TEST_Q`
|
|
174
|
-
pub(crate) fn
|
|
226
|
+
pub(crate) fn from_client_with_activities<ACT>(
|
|
175
227
|
client: impl WorkerClient + 'static,
|
|
176
|
-
wf_tasks: WFT,
|
|
177
228
|
act_tasks: ACT,
|
|
178
229
|
) -> Self
|
|
179
230
|
where
|
|
180
|
-
|
|
181
|
-
ACT: IntoIterator<Item = PollActivityTaskQueueResponse>,
|
|
182
|
-
<WFT as IntoIterator>::IntoIter: Send + 'static,
|
|
231
|
+
ACT: IntoIterator<Item = QueueResponse<PollActivityTaskQueueResponse>>,
|
|
183
232
|
<ACT as IntoIterator>::IntoIter: Send + 'static,
|
|
184
233
|
{
|
|
185
|
-
let
|
|
234
|
+
let wft_stream = stream::pending().boxed();
|
|
186
235
|
let mock_act_poller = mock_poller_from_resps(act_tasks);
|
|
187
|
-
let mock_worker =
|
|
188
|
-
|
|
236
|
+
let mock_worker = MockWorkerInputs {
|
|
237
|
+
wft_stream,
|
|
189
238
|
act_poller: Some(mock_act_poller),
|
|
190
239
|
config: test_worker_cfg().build().unwrap(),
|
|
191
240
|
};
|
|
192
241
|
Self {
|
|
193
242
|
client_bag: client.into(),
|
|
194
|
-
mock_worker,
|
|
243
|
+
inputs: mock_worker,
|
|
244
|
+
outstanding_task_map: None,
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/// Uses the provided task responses and delivers them as quickly as possible when polled.
|
|
249
|
+
/// This is only useful to test buffering, as typically you do not want to pretend that
|
|
250
|
+
/// the server is delivering WFTs super fast for the same run.
|
|
251
|
+
pub(crate) fn from_wft_stream(
|
|
252
|
+
client: impl WorkerClient + 'static,
|
|
253
|
+
stream: impl Stream<Item = PollWorkflowTaskQueueResponse> + Send + 'static,
|
|
254
|
+
) -> Self {
|
|
255
|
+
let wft_stream = stream
|
|
256
|
+
.map(|r| Ok(r.try_into().expect("Mock responses must be valid work")))
|
|
257
|
+
.boxed();
|
|
258
|
+
let mock_worker = MockWorkerInputs {
|
|
259
|
+
wft_stream,
|
|
260
|
+
act_poller: None,
|
|
261
|
+
config: test_worker_cfg().build().unwrap(),
|
|
262
|
+
};
|
|
263
|
+
Self {
|
|
264
|
+
client_bag: client.into(),
|
|
265
|
+
inputs: mock_worker,
|
|
195
266
|
outstanding_task_map: None,
|
|
196
267
|
}
|
|
197
268
|
}
|
|
198
269
|
}
|
|
199
270
|
|
|
271
|
+
// TODO: Un-pub ideally
|
|
200
272
|
pub(crate) fn mock_poller_from_resps<T, I>(tasks: I) -> BoxedPoller<T>
|
|
201
273
|
where
|
|
202
274
|
T: Send + Sync + 'static,
|
|
203
|
-
I: IntoIterator<Item = T
|
|
275
|
+
I: IntoIterator<Item = QueueResponse<T>>,
|
|
204
276
|
<I as IntoIterator>::IntoIter: Send + 'static,
|
|
205
277
|
{
|
|
206
|
-
let mut mock_poller =
|
|
278
|
+
let mut mock_poller = mock_manual_poller();
|
|
207
279
|
let mut tasks = tasks.into_iter();
|
|
208
280
|
mock_poller.expect_poll().returning(move || {
|
|
209
281
|
if let Some(t) = tasks.next() {
|
|
210
|
-
|
|
282
|
+
async move {
|
|
283
|
+
if let Some(f) = t.delay_until {
|
|
284
|
+
f.await;
|
|
285
|
+
}
|
|
286
|
+
Some(Ok(t.resp))
|
|
287
|
+
}
|
|
288
|
+
.boxed()
|
|
211
289
|
} else {
|
|
212
|
-
Some(Err(tonic::Status::cancelled(NO_MORE_WORK_ERROR_MSG)))
|
|
290
|
+
async { Some(Err(tonic::Status::cancelled(NO_MORE_WORK_ERROR_MSG))) }.boxed()
|
|
213
291
|
}
|
|
214
292
|
});
|
|
215
293
|
Box::new(mock_poller) as BoxedPoller<T>
|
|
@@ -249,7 +327,7 @@ where
|
|
|
249
327
|
pub(crate) fn build_multihist_mock_sg(
|
|
250
328
|
hists: impl IntoIterator<Item = FakeWfResponses>,
|
|
251
329
|
enforce_correct_number_of_polls: bool,
|
|
252
|
-
num_expected_fails:
|
|
330
|
+
num_expected_fails: usize,
|
|
253
331
|
) -> MocksHolder {
|
|
254
332
|
let mh = MockPollCfg::new(
|
|
255
333
|
hists.into_iter().collect(),
|
|
@@ -275,11 +353,13 @@ pub(crate) fn single_hist_mock_sg(
|
|
|
275
353
|
pub(crate) struct MockPollCfg {
|
|
276
354
|
pub hists: Vec<FakeWfResponses>,
|
|
277
355
|
pub enforce_correct_number_of_polls: bool,
|
|
278
|
-
pub num_expected_fails:
|
|
356
|
+
pub num_expected_fails: usize,
|
|
357
|
+
pub num_expected_legacy_query_resps: usize,
|
|
279
358
|
pub mock_client: MockWorkerClient,
|
|
280
359
|
/// All calls to fail WFTs must match this predicate
|
|
281
360
|
pub expect_fail_wft_matcher:
|
|
282
361
|
Box<dyn Fn(&TaskToken, &WorkflowTaskFailedCause, &Option<Failure>) -> bool + Send>,
|
|
362
|
+
pub completion_asserts: Option<Box<dyn Fn(&WorkflowTaskCompletion) + Send>>,
|
|
283
363
|
/// If being used with the Rust SDK, this is set true. It ensures pollers will not error out
|
|
284
364
|
/// early with no work, since we cannot know the exact number of times polling will happen.
|
|
285
365
|
/// Instead, they will just block forever.
|
|
@@ -290,14 +370,16 @@ impl MockPollCfg {
|
|
|
290
370
|
pub fn new(
|
|
291
371
|
hists: Vec<FakeWfResponses>,
|
|
292
372
|
enforce_correct_number_of_polls: bool,
|
|
293
|
-
num_expected_fails:
|
|
373
|
+
num_expected_fails: usize,
|
|
294
374
|
) -> Self {
|
|
295
375
|
Self {
|
|
296
376
|
hists,
|
|
297
377
|
enforce_correct_number_of_polls,
|
|
298
378
|
num_expected_fails,
|
|
379
|
+
num_expected_legacy_query_resps: 0,
|
|
299
380
|
mock_client: mock_workflow_client(),
|
|
300
381
|
expect_fail_wft_matcher: Box::new(|_, _, _| true),
|
|
382
|
+
completion_asserts: None,
|
|
301
383
|
using_rust_sdk: false,
|
|
302
384
|
}
|
|
303
385
|
}
|
|
@@ -314,19 +396,75 @@ impl MockPollCfg {
|
|
|
314
396
|
response_batches: resps.into_iter().map(Into::into).collect(),
|
|
315
397
|
}],
|
|
316
398
|
enforce_correct_number_of_polls: true,
|
|
317
|
-
num_expected_fails:
|
|
399
|
+
num_expected_fails: 0,
|
|
400
|
+
num_expected_legacy_query_resps: 0,
|
|
318
401
|
mock_client,
|
|
319
402
|
expect_fail_wft_matcher: Box::new(|_, _, _| true),
|
|
403
|
+
completion_asserts: None,
|
|
320
404
|
using_rust_sdk: false,
|
|
321
405
|
}
|
|
322
406
|
}
|
|
323
407
|
}
|
|
324
408
|
|
|
409
|
+
#[derive(Default, Clone)]
|
|
410
|
+
pub struct OutstandingWFTMap {
|
|
411
|
+
map: Arc<RwLock<BiMap<String, TaskToken>>>,
|
|
412
|
+
waker: Arc<Notify>,
|
|
413
|
+
all_work_delivered: Arc<AtomicBool>,
|
|
414
|
+
}
|
|
415
|
+
impl OutstandingWFTMap {
|
|
416
|
+
fn has_run(&self, run_id: &str) -> bool {
|
|
417
|
+
self.map.read().contains_left(run_id)
|
|
418
|
+
}
|
|
419
|
+
fn put_token(&self, run_id: String, token: TaskToken) {
|
|
420
|
+
self.map.write().insert(run_id, token);
|
|
421
|
+
}
|
|
422
|
+
fn release_token(&self, token: &TaskToken) {
|
|
423
|
+
self.map.write().remove_by_right(token);
|
|
424
|
+
self.waker.notify_one();
|
|
425
|
+
}
|
|
426
|
+
pub fn release_run(&self, run_id: &str) {
|
|
427
|
+
self.map.write().remove_by_left(run_id);
|
|
428
|
+
self.waker.notify_waiters();
|
|
429
|
+
}
|
|
430
|
+
pub fn all_work_delivered(&self) -> bool {
|
|
431
|
+
self.all_work_delivered.load(Ordering::Acquire)
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
struct EnsuresWorkDoneWFTStream {
|
|
436
|
+
inner: UnboundedReceiverStream<ValidPollWFTQResponse>,
|
|
437
|
+
all_work_was_completed: Arc<AtomicBool>,
|
|
438
|
+
}
|
|
439
|
+
impl Stream for EnsuresWorkDoneWFTStream {
|
|
440
|
+
type Item = ValidPollWFTQResponse;
|
|
441
|
+
|
|
442
|
+
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
|
443
|
+
self.inner.poll_next_unpin(cx)
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
impl Drop for EnsuresWorkDoneWFTStream {
|
|
447
|
+
fn drop(&mut self) {
|
|
448
|
+
if !self.all_work_was_completed.load(Ordering::Acquire) && !std::thread::panicking() {
|
|
449
|
+
panic!("Not all workflow tasks were taken from mock!");
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
325
454
|
/// Given an iterable of fake responses, return the mocks & associated data to work with them
|
|
326
455
|
pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
|
|
327
456
|
let mut task_q_resps: BTreeMap<String, VecDeque<_>> = BTreeMap::new();
|
|
328
|
-
let
|
|
329
|
-
|
|
457
|
+
let all_work_delivered = if cfg.enforce_correct_number_of_polls && !cfg.using_rust_sdk {
|
|
458
|
+
Arc::new(AtomicBool::new(false))
|
|
459
|
+
} else {
|
|
460
|
+
Arc::new(AtomicBool::new(true))
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
let outstanding_wf_task_tokens = OutstandingWFTMap {
|
|
464
|
+
map: Arc::new(Default::default()),
|
|
465
|
+
waker: Arc::new(Default::default()),
|
|
466
|
+
all_work_delivered: all_work_delivered.clone(),
|
|
467
|
+
};
|
|
330
468
|
|
|
331
469
|
for hist in cfg.hists {
|
|
332
470
|
let full_hist_info = hist.hist.get_full_history_info().unwrap();
|
|
@@ -342,10 +480,6 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
|
|
|
342
480
|
}
|
|
343
481
|
}
|
|
344
482
|
|
|
345
|
-
if cfg.enforce_correct_number_of_polls && !cfg.using_rust_sdk {
|
|
346
|
-
*correct_num_polls.get_or_insert(0) += hist.response_batches.len();
|
|
347
|
-
}
|
|
348
|
-
|
|
349
483
|
// Convert history batches into poll responses, while also tracking how many times a given
|
|
350
484
|
// history has been returned so we can increment the associated attempt number on the WFT.
|
|
351
485
|
// NOTE: This is hard to use properly with the `AfterEveryReply` testing eviction mode.
|
|
@@ -354,10 +488,10 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
|
|
|
354
488
|
let mut attempts_at_task_num = HashMap::new();
|
|
355
489
|
let responses: Vec<_> = hist
|
|
356
490
|
.response_batches
|
|
357
|
-
.
|
|
358
|
-
.map(|
|
|
359
|
-
let cur_attempt = attempts_at_task_num.entry(
|
|
360
|
-
let mut r = hist_to_poll_resp(&hist.hist, hist.wf_id.clone(),
|
|
491
|
+
.into_iter()
|
|
492
|
+
.map(|response| {
|
|
493
|
+
let cur_attempt = attempts_at_task_num.entry(response.hashable()).or_insert(1);
|
|
494
|
+
let mut r = hist_to_poll_resp(&hist.hist, hist.wf_id.clone(), response, TEST_Q);
|
|
361
495
|
r.attempt = *cur_attempt;
|
|
362
496
|
*cur_attempt += 1;
|
|
363
497
|
r
|
|
@@ -368,103 +502,168 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
|
|
|
368
502
|
task_q_resps.insert(hist.wf_id, tasks);
|
|
369
503
|
}
|
|
370
504
|
|
|
371
|
-
let mut mock_poller = mock_manual_poller();
|
|
372
505
|
// The poller will return history from any workflow runs that do not have currently
|
|
373
506
|
// outstanding tasks.
|
|
374
507
|
let outstanding = outstanding_wf_task_tokens.clone();
|
|
375
|
-
let
|
|
376
|
-
let
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
.times(correct_num_polls.map_or_else(|| RangeFull.into(), Into::<TimesRange>::into))
|
|
380
|
-
.returning(move || {
|
|
508
|
+
let outstanding_wakeup = outstanding.waker.clone();
|
|
509
|
+
let (wft_tx, wft_rx) = unbounded_channel();
|
|
510
|
+
tokio::task::spawn(async move {
|
|
511
|
+
loop {
|
|
381
512
|
let mut resp = None;
|
|
382
|
-
|
|
513
|
+
let mut resp_iter = task_q_resps.iter_mut();
|
|
514
|
+
for (_, tasks) in &mut resp_iter {
|
|
383
515
|
// Must extract run id from a workflow task associated with this workflow
|
|
384
516
|
// TODO: Case where run id changes for same workflow id is not handled here
|
|
385
517
|
if let Some(t) = tasks.get(0) {
|
|
386
518
|
let rid = t.workflow_execution.as_ref().unwrap().run_id.clone();
|
|
387
|
-
if !outstanding.
|
|
519
|
+
if !outstanding.has_run(&rid) {
|
|
388
520
|
let t = tasks.pop_front().unwrap();
|
|
389
|
-
outstanding
|
|
390
|
-
|
|
391
|
-
.insert(rid, TaskToken(t.task_token.clone()));
|
|
392
|
-
resp = Some(Ok(t));
|
|
521
|
+
outstanding.put_token(rid, TaskToken(t.task_token.clone()));
|
|
522
|
+
resp = Some(t);
|
|
393
523
|
break;
|
|
394
524
|
}
|
|
395
525
|
}
|
|
396
526
|
}
|
|
397
|
-
let
|
|
398
|
-
async move {
|
|
399
|
-
if resp.is_some() {
|
|
400
|
-
return resp;
|
|
401
|
-
}
|
|
527
|
+
let no_tasks_for_anyone = resp_iter.next().is_none();
|
|
402
528
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
529
|
+
if let Some(resp) = resp {
|
|
530
|
+
if let Some(d) = resp.delay_until {
|
|
531
|
+
d.await;
|
|
532
|
+
}
|
|
533
|
+
if wft_tx
|
|
534
|
+
.send(
|
|
535
|
+
resp.resp
|
|
536
|
+
.try_into()
|
|
537
|
+
.expect("Mock responses must be valid work"),
|
|
538
|
+
)
|
|
539
|
+
.is_err()
|
|
540
|
+
{
|
|
541
|
+
dbg!("Exiting mock WFT task because rcv half of stream was dropped");
|
|
542
|
+
break;
|
|
413
543
|
}
|
|
414
544
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
545
|
+
|
|
546
|
+
// No more work to do
|
|
547
|
+
if task_q_resps.values().all(|q| q.is_empty()) {
|
|
548
|
+
outstanding
|
|
549
|
+
.all_work_delivered
|
|
550
|
+
.store(true, Ordering::Release);
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if no_tasks_for_anyone {
|
|
555
|
+
tokio::select! {
|
|
556
|
+
_ = outstanding_wakeup.notified() => {}
|
|
557
|
+
_ = tokio::time::sleep(Duration::from_secs(60)) => {}
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
let mock_worker = MockWorkerInputs::new(
|
|
563
|
+
EnsuresWorkDoneWFTStream {
|
|
564
|
+
inner: UnboundedReceiverStream::new(wft_rx),
|
|
565
|
+
all_work_was_completed: all_work_delivered,
|
|
566
|
+
}
|
|
567
|
+
.map(Ok)
|
|
568
|
+
.boxed(),
|
|
569
|
+
);
|
|
418
570
|
|
|
419
571
|
let outstanding = outstanding_wf_task_tokens.clone();
|
|
420
|
-
let outstanding_wakeup = outstanding_wakeup_orig.clone();
|
|
421
572
|
cfg.mock_client
|
|
422
573
|
.expect_complete_workflow_task()
|
|
423
574
|
.returning(move |comp| {
|
|
424
|
-
|
|
425
|
-
|
|
575
|
+
if let Some(ass) = cfg.completion_asserts.as_ref() {
|
|
576
|
+
// tee hee
|
|
577
|
+
ass(&comp)
|
|
578
|
+
}
|
|
579
|
+
outstanding.release_token(&comp.task_token);
|
|
426
580
|
Ok(RespondWorkflowTaskCompletedResponse::default())
|
|
427
581
|
});
|
|
428
582
|
let outstanding = outstanding_wf_task_tokens.clone();
|
|
429
583
|
cfg.mock_client
|
|
430
584
|
.expect_fail_workflow_task()
|
|
431
585
|
.withf(cfg.expect_fail_wft_matcher)
|
|
432
|
-
.times(
|
|
433
|
-
cfg.num_expected_fails
|
|
434
|
-
.map_or_else(|| RangeFull.into(), Into::<TimesRange>::into),
|
|
435
|
-
)
|
|
586
|
+
.times::<TimesRange>(cfg.num_expected_fails.into())
|
|
436
587
|
.returning(move |tt, _, _| {
|
|
437
|
-
outstanding.
|
|
438
|
-
|
|
588
|
+
outstanding.release_token(&tt);
|
|
589
|
+
Ok(Default::default())
|
|
590
|
+
});
|
|
591
|
+
let outstanding = outstanding_wf_task_tokens.clone();
|
|
592
|
+
cfg.mock_client
|
|
593
|
+
.expect_respond_legacy_query()
|
|
594
|
+
.times::<TimesRange>(cfg.num_expected_legacy_query_resps.into())
|
|
595
|
+
.returning(move |tt, _| {
|
|
596
|
+
outstanding.release_token(&tt);
|
|
439
597
|
Ok(Default::default())
|
|
440
598
|
});
|
|
441
599
|
|
|
442
600
|
MocksHolder {
|
|
443
601
|
client_bag: cfg.mock_client.into(),
|
|
444
|
-
mock_worker,
|
|
602
|
+
inputs: mock_worker,
|
|
445
603
|
outstanding_task_map: Some(outstanding_wf_task_tokens),
|
|
446
604
|
}
|
|
447
605
|
}
|
|
448
606
|
|
|
607
|
+
pub struct QueueResponse<T> {
|
|
608
|
+
pub resp: T,
|
|
609
|
+
pub delay_until: Option<BoxFuture<'static, ()>>,
|
|
610
|
+
}
|
|
611
|
+
impl<T> From<T> for QueueResponse<T> {
|
|
612
|
+
fn from(resp: T) -> Self {
|
|
613
|
+
QueueResponse {
|
|
614
|
+
resp,
|
|
615
|
+
delay_until: None,
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
impl From<QueueResponse<PollWorkflowTaskQueueResponse>> for ResponseType {
|
|
620
|
+
fn from(qr: QueueResponse<PollWorkflowTaskQueueResponse>) -> Self {
|
|
621
|
+
ResponseType::Raw(qr.resp)
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
impl<T> Deref for QueueResponse<T> {
|
|
625
|
+
type Target = T;
|
|
626
|
+
|
|
627
|
+
fn deref(&self) -> &Self::Target {
|
|
628
|
+
&self.resp
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
impl<T> DerefMut for QueueResponse<T> {
|
|
632
|
+
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
633
|
+
&mut self.resp
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
449
637
|
pub fn hist_to_poll_resp(
|
|
450
638
|
t: &TestHistoryBuilder,
|
|
451
639
|
wf_id: String,
|
|
452
640
|
response_type: ResponseType,
|
|
453
641
|
task_queue: impl Into<String>,
|
|
454
|
-
) -> PollWorkflowTaskQueueResponse {
|
|
642
|
+
) -> QueueResponse<PollWorkflowTaskQueueResponse> {
|
|
455
643
|
let run_id = t.get_orig_run_id();
|
|
456
644
|
let wf = WorkflowExecution {
|
|
457
645
|
workflow_id: wf_id,
|
|
458
646
|
run_id: run_id.to_string(),
|
|
459
647
|
};
|
|
648
|
+
let mut delay_until = None;
|
|
460
649
|
let hist_info = match response_type {
|
|
461
650
|
ResponseType::ToTaskNum(tn) => t.get_history_info(tn).unwrap(),
|
|
462
651
|
ResponseType::OneTask(tn) => t.get_one_wft(tn).unwrap(),
|
|
463
652
|
ResponseType::AllHistory => t.get_full_history_info().unwrap(),
|
|
653
|
+
ResponseType::Raw(r) => {
|
|
654
|
+
return QueueResponse {
|
|
655
|
+
resp: r,
|
|
656
|
+
delay_until: None,
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
ResponseType::UntilResolved(fut, tn) => {
|
|
660
|
+
delay_until = Some(fut);
|
|
661
|
+
t.get_history_info(tn).unwrap()
|
|
662
|
+
}
|
|
464
663
|
};
|
|
465
664
|
let mut resp = hist_info.as_poll_wft_response(task_queue);
|
|
466
665
|
resp.workflow_execution = Some(wf);
|
|
467
|
-
resp
|
|
666
|
+
QueueResponse { resp, delay_until }
|
|
468
667
|
}
|
|
469
668
|
|
|
470
669
|
type AsserterWithReply<'a> = (
|
|
@@ -472,6 +671,19 @@ type AsserterWithReply<'a> = (
|
|
|
472
671
|
workflow_activation_completion::Status,
|
|
473
672
|
);
|
|
474
673
|
|
|
674
|
+
/// Determines when workflows are kept in the cache or evicted for [poll_and_reply] type tests
|
|
675
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
676
|
+
pub(crate) enum WorkflowCachingPolicy {
|
|
677
|
+
/// Workflows are evicted after each workflow task completion. Note that this is *not* after
|
|
678
|
+
/// each workflow activation - there are often multiple activations per workflow task.
|
|
679
|
+
NonSticky,
|
|
680
|
+
|
|
681
|
+
/// Not a real mode, but good for imitating crashes. Evict workflows after *every* reply,
|
|
682
|
+
/// even if there are pending activations
|
|
683
|
+
#[cfg(test)]
|
|
684
|
+
AfterEveryReply,
|
|
685
|
+
}
|
|
686
|
+
|
|
475
687
|
/// This function accepts a list of asserts and replies to workflow activations to run against the
|
|
476
688
|
/// provided instance of fake core.
|
|
477
689
|
///
|
|
@@ -489,7 +701,7 @@ pub(crate) async fn poll_and_reply<'a>(
|
|
|
489
701
|
|
|
490
702
|
pub(crate) async fn poll_and_reply_clears_outstanding_evicts<'a>(
|
|
491
703
|
worker: &'a Worker,
|
|
492
|
-
outstanding_map: Option<
|
|
704
|
+
outstanding_map: Option<OutstandingWFTMap>,
|
|
493
705
|
eviction_mode: WorkflowCachingPolicy,
|
|
494
706
|
expect_and_reply: &'a [AsserterWithReply<'a>],
|
|
495
707
|
) {
|
|
@@ -515,6 +727,7 @@ pub(crate) async fn poll_and_reply_clears_outstanding_evicts<'a>(
|
|
|
515
727
|
let mut res = worker.poll_workflow_activation().await.unwrap();
|
|
516
728
|
let contains_eviction = res.eviction_index();
|
|
517
729
|
|
|
730
|
+
let mut do_release = false;
|
|
518
731
|
if let Some(eviction_job_ix) = contains_eviction {
|
|
519
732
|
// If the job list has an eviction, make sure it was the last item in the list
|
|
520
733
|
// then remove it, since in the tests we don't explicitly specify evict assertions
|
|
@@ -524,9 +737,7 @@ pub(crate) async fn poll_and_reply_clears_outstanding_evicts<'a>(
|
|
|
524
737
|
"Eviction job was not last job in job list"
|
|
525
738
|
);
|
|
526
739
|
res.jobs.remove(eviction_job_ix);
|
|
527
|
-
|
|
528
|
-
omap.write().remove_by_left(&res.run_id);
|
|
529
|
-
}
|
|
740
|
+
do_release = true;
|
|
530
741
|
}
|
|
531
742
|
|
|
532
743
|
// TODO: Can remove this if?
|
|
@@ -549,6 +760,11 @@ pub(crate) async fn poll_and_reply_clears_outstanding_evicts<'a>(
|
|
|
549
760
|
|
|
550
761
|
worker.complete_workflow_activation(reply).await.unwrap();
|
|
551
762
|
|
|
763
|
+
if do_release {
|
|
764
|
+
if let Some(omap) = outstanding_map.as_ref() {
|
|
765
|
+
omap.release_run(&res.run_id);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
552
768
|
// Restart assertions from the beginning if it was an eviction (and workflow execution
|
|
553
769
|
// isn't over)
|
|
554
770
|
if contains_eviction.is_some() && !ends_execution {
|
|
@@ -560,7 +776,6 @@ pub(crate) async fn poll_and_reply_clears_outstanding_evicts<'a>(
|
|
|
560
776
|
}
|
|
561
777
|
|
|
562
778
|
match eviction_mode {
|
|
563
|
-
WorkflowCachingPolicy::Sticky { .. } => unimplemented!(),
|
|
564
779
|
WorkflowCachingPolicy::NonSticky => (),
|
|
565
780
|
WorkflowCachingPolicy::AfterEveryReply => {
|
|
566
781
|
if evictions < expected_evictions {
|
|
@@ -575,7 +790,7 @@ pub(crate) async fn poll_and_reply_clears_outstanding_evicts<'a>(
|
|
|
575
790
|
}
|
|
576
791
|
|
|
577
792
|
assert_eq!(expected_fail_count, executed_failures.len());
|
|
578
|
-
assert_eq!(worker.outstanding_workflow_tasks(), 0);
|
|
793
|
+
assert_eq!(worker.outstanding_workflow_tasks().await, 0);
|
|
579
794
|
}
|
|
580
795
|
|
|
581
796
|
pub(crate) fn gen_assert_and_reply(
|
|
@@ -615,3 +830,20 @@ macro_rules! job_assert {
|
|
|
615
830
|
}
|
|
616
831
|
};
|
|
617
832
|
}
|
|
833
|
+
|
|
834
|
+
/// Forcibly drive a future a number of times, enforcing it is always returning Pending. This is
|
|
835
|
+
/// useful for ensuring some future has proceeded "enough" before racing it against another future.
|
|
836
|
+
#[macro_export]
|
|
837
|
+
macro_rules! advance_fut {
|
|
838
|
+
($fut:ident) => {
|
|
839
|
+
::futures::pin_mut!($fut);
|
|
840
|
+
{
|
|
841
|
+
let waker = ::futures::task::noop_waker();
|
|
842
|
+
let mut cx = core::task::Context::from_waker(&waker);
|
|
843
|
+
for _ in 0..10 {
|
|
844
|
+
assert_matches!($fut.poll_unpin(&mut cx), core::task::Poll::Pending);
|
|
845
|
+
::tokio::task::yield_now().await;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
}
|