@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
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
//! This module contains very generic helpers that can be used codebase-wide
|
|
2
2
|
|
|
3
3
|
use crate::MetricsContext;
|
|
4
|
-
use
|
|
4
|
+
use futures::{stream, Stream, StreamExt};
|
|
5
|
+
use std::{
|
|
6
|
+
fmt::{Debug, Formatter},
|
|
7
|
+
future::Future,
|
|
8
|
+
sync::Arc,
|
|
9
|
+
};
|
|
10
|
+
use tokio::sync::{AcquireError, Notify, OwnedSemaphorePermit, Semaphore, TryAcquireError};
|
|
5
11
|
|
|
6
12
|
/// Wraps a [Semaphore] with a function call that is fed the available permits any time a permit is
|
|
7
13
|
/// acquired or restored through the provided methods
|
|
14
|
+
#[derive(Clone)]
|
|
8
15
|
pub(crate) struct MeteredSemaphore {
|
|
9
|
-
|
|
10
|
-
max_permits: usize,
|
|
16
|
+
sem: Arc<Semaphore>,
|
|
11
17
|
metrics_ctx: MetricsContext,
|
|
12
18
|
record_fn: fn(&MetricsContext, usize),
|
|
13
19
|
}
|
|
@@ -19,27 +25,142 @@ impl MeteredSemaphore {
|
|
|
19
25
|
record_fn: fn(&MetricsContext, usize),
|
|
20
26
|
) -> Self {
|
|
21
27
|
Self {
|
|
22
|
-
sem: Semaphore::new(inital_permits),
|
|
23
|
-
max_permits: inital_permits,
|
|
28
|
+
sem: Arc::new(Semaphore::new(inital_permits)),
|
|
24
29
|
metrics_ctx,
|
|
25
30
|
record_fn,
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
33
|
|
|
29
|
-
pub
|
|
30
|
-
|
|
34
|
+
pub fn available_permits(&self) -> usize {
|
|
35
|
+
self.sem.available_permits()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub async fn acquire_owned(&self) -> Result<OwnedMeteredSemPermit, AcquireError> {
|
|
39
|
+
let res = self.sem.clone().acquire_owned().await?;
|
|
40
|
+
self.record();
|
|
41
|
+
Ok(OwnedMeteredSemPermit {
|
|
42
|
+
inner: res,
|
|
43
|
+
record_fn: self.record_drop_owned(),
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub fn try_acquire_owned(&self) -> Result<OwnedMeteredSemPermit, TryAcquireError> {
|
|
48
|
+
let res = self.sem.clone().try_acquire_owned()?;
|
|
49
|
+
self.record();
|
|
50
|
+
Ok(OwnedMeteredSemPermit {
|
|
51
|
+
inner: res,
|
|
52
|
+
record_fn: self.record_drop_owned(),
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fn record(&self) {
|
|
31
57
|
(self.record_fn)(&self.metrics_ctx, self.sem.available_permits());
|
|
32
|
-
res
|
|
33
58
|
}
|
|
34
59
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
fn record_drop_owned(&self) -> Box<dyn Fn() + Send + Sync> {
|
|
61
|
+
let rcf = self.record_fn;
|
|
62
|
+
let mets = self.metrics_ctx.clone();
|
|
63
|
+
let sem = self.sem.clone();
|
|
64
|
+
Box::new(move || rcf(&mets, sem.available_permits() + 1))
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/// Wraps an [OwnedSemaphorePermit] to update metrics when it's dropped
|
|
69
|
+
pub(crate) struct OwnedMeteredSemPermit {
|
|
70
|
+
inner: OwnedSemaphorePermit,
|
|
71
|
+
record_fn: Box<dyn Fn() + Send + Sync>,
|
|
72
|
+
}
|
|
73
|
+
impl Drop for OwnedMeteredSemPermit {
|
|
74
|
+
fn drop(&mut self) {
|
|
75
|
+
(self.record_fn)()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
impl Debug for OwnedMeteredSemPermit {
|
|
79
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
80
|
+
self.inner.fmt(f)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// From the input stream, create a new stream which only pulls from the input stream when allowed.
|
|
85
|
+
/// When allowed is determined by the passed in `proceeder` which must return a future every time
|
|
86
|
+
/// it's called. The input stream is only pulled from when that future resolves.
|
|
87
|
+
pub(crate) fn stream_when_allowed<S, F, FF>(
|
|
88
|
+
input: S,
|
|
89
|
+
proceeder: FF,
|
|
90
|
+
) -> impl Stream<Item = (S::Item, F::Output)>
|
|
91
|
+
where
|
|
92
|
+
S: Stream + Send + 'static,
|
|
93
|
+
F: Future,
|
|
94
|
+
FF: FnMut() -> F,
|
|
95
|
+
{
|
|
96
|
+
let acceptable_notify = Arc::new(Notify::new());
|
|
97
|
+
acceptable_notify.notify_one();
|
|
98
|
+
let stream = stream::unfold(
|
|
99
|
+
(proceeder, input.boxed()),
|
|
100
|
+
|(mut proceeder, mut input)| async {
|
|
101
|
+
let v = proceeder().await;
|
|
102
|
+
input.next().await.map(|i| ((i, v), (proceeder, input)))
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
stream
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
macro_rules! dbg_panic {
|
|
109
|
+
($($arg:tt)*) => {
|
|
110
|
+
error!($($arg)*);
|
|
111
|
+
debug_assert!(true, $($arg)*);
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
pub(crate) use dbg_panic;
|
|
115
|
+
|
|
116
|
+
#[cfg(test)]
|
|
117
|
+
mod tests {
|
|
118
|
+
use super::*;
|
|
119
|
+
use futures::pin_mut;
|
|
120
|
+
use std::{cell::RefCell, task::Poll};
|
|
121
|
+
use tokio::sync::mpsc::unbounded_channel;
|
|
122
|
+
|
|
123
|
+
// This is fine. Test only / guaranteed to happen serially.
|
|
124
|
+
#[allow(clippy::await_holding_refcell_ref)]
|
|
125
|
+
#[test]
|
|
126
|
+
fn stream_when_allowed_works() {
|
|
127
|
+
let inputs = stream::iter([1, 2, 3]);
|
|
128
|
+
let (allow_tx, allow_rx) = unbounded_channel();
|
|
129
|
+
let allow_rx = RefCell::new(allow_rx);
|
|
130
|
+
let when_allowed = stream_when_allowed(inputs, || async {
|
|
131
|
+
allow_rx.borrow_mut().recv().await.unwrap()
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
let waker = futures::task::noop_waker_ref();
|
|
135
|
+
let mut cx = std::task::Context::from_waker(waker);
|
|
136
|
+
pin_mut!(when_allowed);
|
|
137
|
+
|
|
138
|
+
allow_tx.send(()).unwrap();
|
|
139
|
+
assert_eq!(
|
|
140
|
+
when_allowed.poll_next_unpin(&mut cx),
|
|
141
|
+
Poll::Ready(Some((1, ())))
|
|
142
|
+
);
|
|
143
|
+
// Now, it won't be ready
|
|
144
|
+
for _ in 1..10 {
|
|
145
|
+
assert_eq!(when_allowed.poll_next_unpin(&mut cx), Poll::Pending);
|
|
146
|
+
}
|
|
147
|
+
allow_tx.send(()).unwrap();
|
|
148
|
+
assert_eq!(
|
|
149
|
+
when_allowed.poll_next_unpin(&mut cx),
|
|
150
|
+
Poll::Ready(Some((2, ())))
|
|
151
|
+
);
|
|
152
|
+
for _ in 1..10 {
|
|
153
|
+
assert_eq!(when_allowed.poll_next_unpin(&mut cx), Poll::Pending);
|
|
154
|
+
}
|
|
155
|
+
allow_tx.send(()).unwrap();
|
|
156
|
+
assert_eq!(
|
|
157
|
+
when_allowed.poll_next_unpin(&mut cx),
|
|
158
|
+
Poll::Ready(Some((3, ())))
|
|
159
|
+
);
|
|
160
|
+
for _ in 1..10 {
|
|
161
|
+
assert_eq!(when_allowed.poll_next_unpin(&mut cx), Poll::Pending);
|
|
43
162
|
}
|
|
163
|
+
allow_tx.send(()).unwrap();
|
|
164
|
+
assert_eq!(when_allowed.poll_next_unpin(&mut cx), Poll::Ready(None));
|
|
44
165
|
}
|
|
45
166
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
-
job_assert,
|
|
2
|
+
advance_fut, job_assert,
|
|
3
3
|
test_help::{
|
|
4
|
-
build_fake_worker, canned_histories, gen_assert_and_reply,
|
|
5
|
-
|
|
4
|
+
build_fake_worker, build_mock_pollers, canned_histories, gen_assert_and_reply,
|
|
5
|
+
mock_manual_poller, mock_poller, mock_poller_from_resps, mock_worker, poll_and_reply,
|
|
6
|
+
single_hist_mock_sg, test_worker_cfg, MockPollCfg, MockWorkerInputs, MocksHolder,
|
|
7
|
+
ResponseType, WorkflowCachingPolicy, TEST_Q,
|
|
6
8
|
},
|
|
7
9
|
worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
|
|
8
|
-
workflow::WorkflowCachingPolicy::NonSticky,
|
|
9
10
|
ActivityHeartbeat, Worker, WorkerConfigBuilder,
|
|
10
11
|
};
|
|
11
12
|
use futures::FutureExt;
|
|
@@ -13,9 +14,14 @@ use std::{
|
|
|
13
14
|
cell::RefCell,
|
|
14
15
|
collections::{hash_map::Entry, HashMap, VecDeque},
|
|
15
16
|
rc::Rc,
|
|
16
|
-
sync::
|
|
17
|
+
sync::{
|
|
18
|
+
atomic::{AtomicUsize, Ordering},
|
|
19
|
+
Arc,
|
|
20
|
+
},
|
|
17
21
|
time::Duration,
|
|
18
22
|
};
|
|
23
|
+
use temporal_client::WorkflowOptions;
|
|
24
|
+
use temporal_sdk::{ActivityOptions, WfContext};
|
|
19
25
|
use temporal_sdk_core_api::Worker as WorkerTrait;
|
|
20
26
|
use temporal_sdk_core_protos::{
|
|
21
27
|
coresdk::{
|
|
@@ -26,16 +32,22 @@ use temporal_sdk_core_protos::{
|
|
|
26
32
|
ActivityCancellationType, CompleteWorkflowExecution, RequestCancelActivity,
|
|
27
33
|
ScheduleActivity,
|
|
28
34
|
},
|
|
35
|
+
workflow_completion::WorkflowActivationCompletion,
|
|
29
36
|
ActivityTaskCompletion,
|
|
30
37
|
},
|
|
31
|
-
temporal::api::
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
temporal::api::{
|
|
39
|
+
command::v1::command::Attributes,
|
|
40
|
+
enums::v1::EventType,
|
|
41
|
+
workflowservice::v1::{
|
|
42
|
+
PollActivityTaskQueueResponse, RecordActivityTaskHeartbeatResponse,
|
|
43
|
+
RespondActivityTaskCanceledResponse, RespondActivityTaskCompletedResponse,
|
|
44
|
+
RespondActivityTaskFailedResponse, RespondWorkflowTaskCompletedResponse,
|
|
45
|
+
},
|
|
35
46
|
},
|
|
47
|
+
TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE,
|
|
36
48
|
};
|
|
37
|
-
use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd};
|
|
38
|
-
use tokio::{
|
|
49
|
+
use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd, TestWorker};
|
|
50
|
+
use tokio::{sync::Barrier, time::sleep};
|
|
39
51
|
|
|
40
52
|
#[tokio::test]
|
|
41
53
|
async fn max_activities_respected() {
|
|
@@ -77,23 +89,18 @@ async fn max_activities_respected() {
|
|
|
77
89
|
// We allow two outstanding activities, therefore first two polls should return right away
|
|
78
90
|
let r1 = worker.poll_activity_task().await.unwrap();
|
|
79
91
|
let _r2 = worker.poll_activity_task().await.unwrap();
|
|
80
|
-
// Third should block until we complete one of the first two
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
last_finisher.store(2, Ordering::SeqCst);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
// So that we know we blocked
|
|
96
|
-
assert_eq!(last_finisher.load(Ordering::Acquire), 2);
|
|
92
|
+
// Third poll should block until we complete one of the first two. To ensure this, manually
|
|
93
|
+
// poll it a bunch to see it's not resolving.
|
|
94
|
+
let poll_fut = worker.poll_activity_task();
|
|
95
|
+
advance_fut!(poll_fut);
|
|
96
|
+
worker
|
|
97
|
+
.complete_activity_task(ActivityTaskCompletion {
|
|
98
|
+
task_token: r1.task_token,
|
|
99
|
+
result: Some(ActivityExecutionResult::ok(vec![1].into())),
|
|
100
|
+
})
|
|
101
|
+
.await
|
|
102
|
+
.unwrap();
|
|
103
|
+
poll_fut.await.unwrap();
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
#[tokio::test]
|
|
@@ -102,7 +109,7 @@ async fn activity_not_found_returns_ok() {
|
|
|
102
109
|
// Mock won't even be called, since we weren't tracking activity
|
|
103
110
|
mock_client.expect_complete_activity_task().times(0);
|
|
104
111
|
|
|
105
|
-
let core = mock_worker(MocksHolder::
|
|
112
|
+
let core = mock_worker(MocksHolder::from_client_with_activities(mock_client, []));
|
|
106
113
|
|
|
107
114
|
core.complete_activity_task(ActivityTaskCompletion {
|
|
108
115
|
task_token: vec![1],
|
|
@@ -133,22 +140,23 @@ async fn heartbeats_report_cancels_only_once() {
|
|
|
133
140
|
.times(1)
|
|
134
141
|
.returning(|_, _| Ok(RespondActivityTaskCanceledResponse::default()));
|
|
135
142
|
|
|
136
|
-
let core = mock_worker(MocksHolder::
|
|
143
|
+
let core = mock_worker(MocksHolder::from_client_with_activities(
|
|
137
144
|
mock_client,
|
|
138
|
-
[],
|
|
139
145
|
[
|
|
140
146
|
PollActivityTaskQueueResponse {
|
|
141
147
|
task_token: vec![1],
|
|
142
148
|
activity_id: "act1".to_string(),
|
|
143
149
|
heartbeat_timeout: Some(Duration::from_millis(1).into()),
|
|
144
150
|
..Default::default()
|
|
145
|
-
}
|
|
151
|
+
}
|
|
152
|
+
.into(),
|
|
146
153
|
PollActivityTaskQueueResponse {
|
|
147
154
|
task_token: vec![2],
|
|
148
155
|
activity_id: "act2".to_string(),
|
|
149
156
|
heartbeat_timeout: Some(Duration::from_millis(1).into()),
|
|
150
157
|
..Default::default()
|
|
151
|
-
}
|
|
158
|
+
}
|
|
159
|
+
.into(),
|
|
152
160
|
],
|
|
153
161
|
));
|
|
154
162
|
|
|
@@ -248,7 +256,7 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
248
256
|
.times(1)
|
|
249
257
|
.returning(|_, _| async { Ok(RespondActivityTaskCompletedResponse::default()) }.boxed());
|
|
250
258
|
|
|
251
|
-
let mw =
|
|
259
|
+
let mw = MockWorkerInputs {
|
|
252
260
|
act_poller: Some(Box::from(mock_poller)),
|
|
253
261
|
..Default::default()
|
|
254
262
|
};
|
|
@@ -300,7 +308,7 @@ async fn activity_poll_timeout_retries() {
|
|
|
300
308
|
}))
|
|
301
309
|
}
|
|
302
310
|
});
|
|
303
|
-
let mw =
|
|
311
|
+
let mw = MockWorkerInputs {
|
|
304
312
|
act_poller: Some(Box::from(mock_act_poller)),
|
|
305
313
|
..Default::default()
|
|
306
314
|
};
|
|
@@ -430,7 +438,7 @@ async fn activity_timeout_no_double_resolve() {
|
|
|
430
438
|
|
|
431
439
|
poll_and_reply(
|
|
432
440
|
&core,
|
|
433
|
-
NonSticky,
|
|
441
|
+
WorkflowCachingPolicy::NonSticky,
|
|
434
442
|
&[
|
|
435
443
|
gen_assert_and_reply(
|
|
436
444
|
&job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
|
|
@@ -490,41 +498,34 @@ async fn can_heartbeat_acts_during_shutdown() {
|
|
|
490
498
|
.times(1)
|
|
491
499
|
.returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
|
|
492
500
|
|
|
493
|
-
let core = mock_worker(MocksHolder::
|
|
501
|
+
let core = mock_worker(MocksHolder::from_client_with_activities(
|
|
494
502
|
mock_client,
|
|
495
|
-
[],
|
|
496
503
|
[PollActivityTaskQueueResponse {
|
|
497
504
|
task_token: vec![1],
|
|
498
505
|
activity_id: "act1".to_string(),
|
|
499
506
|
heartbeat_timeout: Some(Duration::from_millis(1).into()),
|
|
500
507
|
..Default::default()
|
|
501
|
-
}
|
|
508
|
+
}
|
|
509
|
+
.into()],
|
|
502
510
|
));
|
|
503
511
|
|
|
504
512
|
let act = core.poll_activity_task().await.unwrap();
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
};
|
|
511
|
-
let complete_fut = async {
|
|
512
|
-
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
513
|
-
task_token: act.task_token.clone(),
|
|
513
|
+
// Make sure shutdown has progressed before trying to record heartbeat / complete
|
|
514
|
+
let shutdown_fut = core.shutdown();
|
|
515
|
+
advance_fut!(shutdown_fut);
|
|
516
|
+
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
517
|
+
task_token: act.task_token.clone(),
|
|
514
518
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
+
details: vec![vec![1_u8, 2, 3].into()],
|
|
520
|
+
});
|
|
521
|
+
core.complete_activity_task(ActivityTaskCompletion {
|
|
522
|
+
task_token: act.task_token,
|
|
519
523
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
};
|
|
526
|
-
join!(shutdown_fut, complete_fut);
|
|
527
|
-
assert_eq!(&complete_order.into_inner(), &[2, 1])
|
|
524
|
+
result: Some(ActivityExecutionResult::ok(vec![1].into())),
|
|
525
|
+
})
|
|
526
|
+
.await
|
|
527
|
+
.unwrap();
|
|
528
|
+
shutdown_fut.await;
|
|
528
529
|
}
|
|
529
530
|
|
|
530
531
|
/// Verifies that if a user has tried to record a heartbeat and then immediately after failed the
|
|
@@ -550,15 +551,15 @@ async fn complete_act_with_fail_flushes_heartbeat() {
|
|
|
550
551
|
.times(1)
|
|
551
552
|
.returning(|_, _| Ok(RespondActivityTaskFailedResponse::default()));
|
|
552
553
|
|
|
553
|
-
let core = mock_worker(MocksHolder::
|
|
554
|
+
let core = mock_worker(MocksHolder::from_client_with_activities(
|
|
554
555
|
mock_client,
|
|
555
|
-
[],
|
|
556
556
|
[PollActivityTaskQueueResponse {
|
|
557
557
|
task_token: vec![1],
|
|
558
558
|
activity_id: "act1".to_string(),
|
|
559
559
|
heartbeat_timeout: Some(Duration::from_secs(10).into()),
|
|
560
560
|
..Default::default()
|
|
561
|
-
}
|
|
561
|
+
}
|
|
562
|
+
.into()],
|
|
562
563
|
));
|
|
563
564
|
|
|
564
565
|
let act = core.poll_activity_task().await.unwrap();
|
|
@@ -600,9 +601,203 @@ async fn max_tq_acts_set_passed_to_poll_properly() {
|
|
|
600
601
|
.namespace("enchi")
|
|
601
602
|
.task_queue("cat")
|
|
602
603
|
.max_concurrent_at_polls(1_usize)
|
|
604
|
+
.worker_build_id("test_bin_id")
|
|
603
605
|
.max_task_queue_activities_per_second(rate)
|
|
604
606
|
.build()
|
|
605
607
|
.unwrap();
|
|
606
608
|
let worker = Worker::new_test(cfg, mock_client);
|
|
607
609
|
worker.poll_activity_task().await.unwrap();
|
|
608
610
|
}
|
|
611
|
+
|
|
612
|
+
/// This test verifies that activity tasks which come as replies to completing a WFT are properly
|
|
613
|
+
/// delivered via polling.
|
|
614
|
+
#[tokio::test]
|
|
615
|
+
async fn activity_tasks_from_completion_are_delivered() {
|
|
616
|
+
let wfid = "fake_wf_id";
|
|
617
|
+
let mut t = TestHistoryBuilder::default();
|
|
618
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
619
|
+
t.add_full_wf_task();
|
|
620
|
+
let schedid = t.add_activity_task_scheduled("act_id");
|
|
621
|
+
let startid = t.add_activity_task_started(schedid);
|
|
622
|
+
t.add_activity_task_completed(schedid, startid, b"hi".into());
|
|
623
|
+
t.add_full_wf_task();
|
|
624
|
+
t.add_workflow_execution_completed();
|
|
625
|
+
|
|
626
|
+
let mut mock = mock_workflow_client();
|
|
627
|
+
mock.expect_complete_workflow_task()
|
|
628
|
+
.times(1)
|
|
629
|
+
.returning(move |_| {
|
|
630
|
+
Ok(RespondWorkflowTaskCompletedResponse {
|
|
631
|
+
workflow_task: None,
|
|
632
|
+
activity_tasks: vec![PollActivityTaskQueueResponse {
|
|
633
|
+
task_token: vec![1],
|
|
634
|
+
activity_id: "act1".to_string(),
|
|
635
|
+
..Default::default()
|
|
636
|
+
}],
|
|
637
|
+
})
|
|
638
|
+
});
|
|
639
|
+
mock.expect_complete_activity_task()
|
|
640
|
+
.times(1)
|
|
641
|
+
.returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
|
|
642
|
+
let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
|
|
643
|
+
let mut mock_poller = mock_manual_poller();
|
|
644
|
+
mock_poller
|
|
645
|
+
.expect_poll()
|
|
646
|
+
.returning(|| futures::future::pending().boxed());
|
|
647
|
+
mock.set_act_poller(Box::new(mock_poller));
|
|
648
|
+
mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
|
|
649
|
+
let core = mock_worker(mock);
|
|
650
|
+
|
|
651
|
+
let wf_task = core.poll_workflow_activation().await.unwrap();
|
|
652
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
653
|
+
wf_task.run_id,
|
|
654
|
+
ScheduleActivity {
|
|
655
|
+
seq: 1,
|
|
656
|
+
activity_id: "act_id".to_string(),
|
|
657
|
+
cancellation_type: ActivityCancellationType::TryCancel as i32,
|
|
658
|
+
..Default::default()
|
|
659
|
+
}
|
|
660
|
+
.into(),
|
|
661
|
+
))
|
|
662
|
+
.await
|
|
663
|
+
.unwrap();
|
|
664
|
+
|
|
665
|
+
// We should see the activity when we poll now
|
|
666
|
+
let act_task = core.poll_activity_task().await.unwrap();
|
|
667
|
+
assert_eq!(act_task.task_token, vec![1]);
|
|
668
|
+
|
|
669
|
+
core.complete_activity_task(ActivityTaskCompletion {
|
|
670
|
+
task_token: act_task.task_token.clone(),
|
|
671
|
+
result: Some(ActivityExecutionResult::ok("hi".into())),
|
|
672
|
+
})
|
|
673
|
+
.await
|
|
674
|
+
.unwrap();
|
|
675
|
+
|
|
676
|
+
core.shutdown().await;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
#[tokio::test]
|
|
680
|
+
async fn activity_tasks_from_completion_reserve_slots() {
|
|
681
|
+
let wf_id = "fake_wf_id";
|
|
682
|
+
let mut t = TestHistoryBuilder::default();
|
|
683
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
684
|
+
t.add_full_wf_task();
|
|
685
|
+
let schedid = t.add_activity_task_scheduled("1");
|
|
686
|
+
let startid = t.add_activity_task_started(schedid);
|
|
687
|
+
t.add_activity_task_completed(schedid, startid, b"hi".into());
|
|
688
|
+
t.add_full_wf_task();
|
|
689
|
+
let schedid = t.add_activity_task_scheduled("2");
|
|
690
|
+
let startid = t.add_activity_task_started(schedid);
|
|
691
|
+
t.add_activity_task_completed(schedid, startid, b"hi".into());
|
|
692
|
+
t.add_full_wf_task();
|
|
693
|
+
t.add_workflow_execution_completed();
|
|
694
|
+
|
|
695
|
+
let mut mock = mock_workflow_client();
|
|
696
|
+
// Set up two tasks to be returned via normal activity polling
|
|
697
|
+
let act_tasks = VecDeque::from(vec![
|
|
698
|
+
PollActivityTaskQueueResponse {
|
|
699
|
+
task_token: vec![1],
|
|
700
|
+
activity_id: "act1".to_string(),
|
|
701
|
+
..Default::default()
|
|
702
|
+
}
|
|
703
|
+
.into(),
|
|
704
|
+
PollActivityTaskQueueResponse {
|
|
705
|
+
task_token: vec![2],
|
|
706
|
+
activity_id: "act2".to_string(),
|
|
707
|
+
..Default::default()
|
|
708
|
+
}
|
|
709
|
+
.into(),
|
|
710
|
+
]);
|
|
711
|
+
mock.expect_complete_activity_task()
|
|
712
|
+
.times(2)
|
|
713
|
+
.returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
|
|
714
|
+
let barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
|
|
715
|
+
let mut mh = MockPollCfg::from_resp_batches(
|
|
716
|
+
wf_id,
|
|
717
|
+
t,
|
|
718
|
+
[
|
|
719
|
+
ResponseType::ToTaskNum(1),
|
|
720
|
+
// We don't want the second task to be delivered until *after* the activity tasks
|
|
721
|
+
// have been completed, so that the second activity schedule will have slots available
|
|
722
|
+
ResponseType::UntilResolved(
|
|
723
|
+
async {
|
|
724
|
+
barr.wait().await;
|
|
725
|
+
barr.wait().await;
|
|
726
|
+
}
|
|
727
|
+
.boxed(),
|
|
728
|
+
2,
|
|
729
|
+
),
|
|
730
|
+
ResponseType::AllHistory,
|
|
731
|
+
],
|
|
732
|
+
mock,
|
|
733
|
+
);
|
|
734
|
+
mh.completion_asserts = Some(Box::new(|wftc| {
|
|
735
|
+
// Make sure when we see the completion with the schedule act command that it does
|
|
736
|
+
// not have the eager execution flag set the first time, and does the second.
|
|
737
|
+
if let Some(Attributes::ScheduleActivityTaskCommandAttributes(attrs)) =
|
|
738
|
+
wftc.commands.get(0).and_then(|cmd| cmd.attributes.as_ref())
|
|
739
|
+
{
|
|
740
|
+
if attrs.activity_id == "1" {
|
|
741
|
+
assert!(!attrs.request_eager_execution);
|
|
742
|
+
} else {
|
|
743
|
+
assert!(attrs.request_eager_execution);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}));
|
|
747
|
+
let mut mock = build_mock_pollers(mh);
|
|
748
|
+
mock.worker_cfg(|cfg| {
|
|
749
|
+
cfg.max_cached_workflows = 2;
|
|
750
|
+
cfg.max_outstanding_activities = 2;
|
|
751
|
+
});
|
|
752
|
+
mock.set_act_poller(mock_poller_from_resps(act_tasks));
|
|
753
|
+
let core = Arc::new(mock_worker(mock));
|
|
754
|
+
let mut worker = TestWorker::new(core.clone(), TEST_Q.to_string());
|
|
755
|
+
|
|
756
|
+
// First poll for activities twice, occupying both slots
|
|
757
|
+
let at1 = core.poll_activity_task().await.unwrap();
|
|
758
|
+
let at2 = core.poll_activity_task().await.unwrap();
|
|
759
|
+
|
|
760
|
+
worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
|
|
761
|
+
ctx.activity(ActivityOptions {
|
|
762
|
+
activity_type: "act1".to_string(),
|
|
763
|
+
..Default::default()
|
|
764
|
+
})
|
|
765
|
+
.await;
|
|
766
|
+
ctx.activity(ActivityOptions {
|
|
767
|
+
activity_type: "act2".to_string(),
|
|
768
|
+
..Default::default()
|
|
769
|
+
})
|
|
770
|
+
.await;
|
|
771
|
+
Ok(().into())
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
worker
|
|
775
|
+
.submit_wf(
|
|
776
|
+
wf_id.to_owned(),
|
|
777
|
+
DEFAULT_WORKFLOW_TYPE,
|
|
778
|
+
vec![],
|
|
779
|
+
WorkflowOptions::default(),
|
|
780
|
+
)
|
|
781
|
+
.await
|
|
782
|
+
.unwrap();
|
|
783
|
+
let act_completer = async {
|
|
784
|
+
barr.wait().await;
|
|
785
|
+
core.complete_activity_task(ActivityTaskCompletion {
|
|
786
|
+
task_token: at1.task_token,
|
|
787
|
+
result: Some(ActivityExecutionResult::ok("hi".into())),
|
|
788
|
+
})
|
|
789
|
+
.await
|
|
790
|
+
.unwrap();
|
|
791
|
+
core.complete_activity_task(ActivityTaskCompletion {
|
|
792
|
+
task_token: at2.task_token,
|
|
793
|
+
result: Some(ActivityExecutionResult::ok("hi".into())),
|
|
794
|
+
})
|
|
795
|
+
.await
|
|
796
|
+
.unwrap();
|
|
797
|
+
barr.wait().await;
|
|
798
|
+
};
|
|
799
|
+
// This wf poll should *not* set the flag that it wants tasks back since both slots are
|
|
800
|
+
// occupied
|
|
801
|
+
let run_fut = async { worker.run_until_done().await.unwrap() };
|
|
802
|
+
tokio::join!(run_fut, act_completer);
|
|
803
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
replay::DEFAULT_WORKFLOW_TYPE,
|
|
3
3
|
test_help::{canned_histories, mock_sdk, MockPollCfg, ResponseType},
|
|
4
|
-
worker::client::mocks::mock_workflow_client,
|
|
5
|
-
workflow::managed_wf::ManagedWFFunc,
|
|
4
|
+
worker::{client::mocks::mock_workflow_client, ManagedWFFunc},
|
|
6
5
|
};
|
|
7
6
|
use temporal_client::WorkflowOptions;
|
|
8
7
|
use temporal_sdk::{ChildWorkflowOptions, Signal, WfContext, WorkflowFunction, WorkflowResult};
|
|
@@ -34,7 +34,7 @@ async fn test_panic_wf_task_rejected_properly() {
|
|
|
34
34
|
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 2], mock);
|
|
35
35
|
// We should see one wft failure which has unspecified cause, since panics don't have a defined
|
|
36
36
|
// type.
|
|
37
|
-
mh.num_expected_fails =
|
|
37
|
+
mh.num_expected_fails = 1;
|
|
38
38
|
mh.expect_fail_wft_matcher =
|
|
39
39
|
Box::new(|_, cause, _| matches!(cause, WorkflowTaskFailedCause::Unspecified));
|
|
40
40
|
let mut worker = mock_sdk(mh);
|
|
@@ -71,7 +71,7 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
|
|
|
71
71
|
mock,
|
|
72
72
|
);
|
|
73
73
|
// We should see one wft failure which has nondeterminism cause
|
|
74
|
-
mh.num_expected_fails =
|
|
74
|
+
mh.num_expected_fails = 1;
|
|
75
75
|
mh.expect_fail_wft_matcher =
|
|
76
76
|
Box::new(|_, cause, _| matches!(cause, WorkflowTaskFailedCause::NonDeterministicError));
|
|
77
77
|
let mut worker = mock_sdk_cfg(mh, |cfg| {
|
|
@@ -12,8 +12,8 @@ use std::{
|
|
|
12
12
|
use temporal_client::WorkflowOptions;
|
|
13
13
|
use temporal_sdk::{ActContext, LocalActivityOptions, WfContext, WorkflowResult};
|
|
14
14
|
use temporal_sdk_core_protos::{
|
|
15
|
-
coresdk::
|
|
16
|
-
temporal::api::{enums::v1::EventType, failure::v1::Failure},
|
|
15
|
+
coresdk::AsJsonPayloadExt,
|
|
16
|
+
temporal::api::{common::v1::RetryPolicy, enums::v1::EventType, failure::v1::Failure},
|
|
17
17
|
};
|
|
18
18
|
use tokio::sync::Barrier;
|
|
19
19
|
|
|
@@ -159,12 +159,13 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
159
159
|
|
|
160
160
|
let wf_id = "fakeid";
|
|
161
161
|
let mock = mock_workflow_client();
|
|
162
|
-
|
|
163
|
-
// and might poll an extra time
|
|
164
|
-
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 2, 2, 2], mock);
|
|
162
|
+
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 2, 2], mock);
|
|
165
163
|
mh.enforce_correct_number_of_polls = false;
|
|
166
|
-
let mut worker = mock_sdk_cfg(mh, |wc|
|
|
167
|
-
|
|
164
|
+
let mut worker = mock_sdk_cfg(mh, |wc| {
|
|
165
|
+
wc.max_cached_workflows = 1;
|
|
166
|
+
wc.max_outstanding_workflow_tasks = 1;
|
|
167
|
+
});
|
|
168
|
+
let core = worker.core_worker.clone();
|
|
168
169
|
|
|
169
170
|
let shutdown_barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
|
|
170
171
|
|