@temporalio/core-bridge 1.7.4 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +245 -247
- package/Cargo.toml +1 -1
- package/lib/errors.d.ts +9 -0
- package/lib/errors.js +13 -0
- package/lib/errors.js.map +1 -1
- package/lib/index.d.ts +19 -3
- package/lib/index.js.map +1 -1
- 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/.github/workflows/heavy.yml +1 -1
- package/sdk-core/.github/workflows/semgrep.yml +25 -0
- package/sdk-core/README.md +2 -0
- package/sdk-core/cargo-tokio-console.sh +5 -0
- package/sdk-core/client/src/lib.rs +6 -41
- package/sdk-core/client/src/raw.rs +9 -0
- package/sdk-core/client/src/retry.rs +0 -16
- package/sdk-core/core/Cargo.toml +9 -5
- package/sdk-core/core/src/abstractions.rs +7 -75
- package/sdk-core/core/src/core_tests/activity_tasks.rs +16 -8
- package/sdk-core/core/src/core_tests/local_activities.rs +97 -5
- package/sdk-core/core/src/core_tests/mod.rs +1 -1
- package/sdk-core/core/src/core_tests/workers.rs +16 -16
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +247 -28
- package/sdk-core/core/src/lib.rs +2 -3
- package/sdk-core/core/src/pollers/mod.rs +30 -3
- package/sdk-core/core/src/pollers/poll_buffer.rs +166 -77
- package/sdk-core/core/src/protosext/mod.rs +4 -8
- package/sdk-core/core/src/replay/mod.rs +1 -1
- package/sdk-core/core/src/telemetry/metrics.rs +9 -0
- package/sdk-core/core/src/telemetry/mod.rs +3 -0
- package/sdk-core/core/src/test_help/mod.rs +9 -16
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +6 -31
- package/sdk-core/core/src/worker/activities/local_activities.rs +214 -110
- package/sdk-core/core/src/worker/activities.rs +72 -47
- package/sdk-core/core/src/worker/client/mocks.rs +1 -1
- package/sdk-core/core/src/worker/client.rs +45 -32
- package/sdk-core/core/src/worker/mod.rs +170 -122
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +0 -4
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +9 -2
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +9 -2
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +6 -3
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +74 -22
- package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +3 -2
- package/sdk-core/core/src/worker/workflow/managed_run.rs +16 -3
- package/sdk-core/core/src/worker/workflow/mod.rs +13 -22
- package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -0
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +4 -7
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +38 -8
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +1 -0
- package/sdk-core/core-api/src/worker.rs +43 -2
- package/sdk-core/protos/api_upstream/Makefile +1 -1
- package/sdk-core/protos/api_upstream/buf.yaml +1 -6
- package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +12 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +9 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +19 -0
- package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +5 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +36 -4
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +24 -7
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +4 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +76 -44
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +23 -1
- package/sdk-core/protos/google/rpc/status.proto +52 -0
- package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +16 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +6 -0
- package/sdk-core/sdk/src/lib.rs +31 -10
- package/sdk-core/sdk/src/workflow_future.rs +7 -5
- package/sdk-core/sdk-core-protos/src/history_builder.rs +2 -0
- package/sdk-core/sdk-core-protos/src/history_info.rs +1 -0
- package/sdk-core/sdk-core-protos/src/lib.rs +82 -73
- package/sdk-core/test-utils/Cargo.toml +1 -1
- package/sdk-core/test-utils/src/lib.rs +50 -37
- package/sdk-core/tests/integ_tests/metrics_tests.rs +143 -10
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +26 -15
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +2 -2
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +5 -1
- package/sdk-core/tests/integ_tests/workflow_tests.rs +1 -0
- package/src/conversions.rs +9 -2
- package/src/runtime.rs +5 -7
- package/ts/errors.ts +15 -0
- package/ts/index.ts +22 -4
|
@@ -1,51 +1,65 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
+
abstractions::{dbg_panic, MeteredSemaphore, OwnedMeteredSemPermit},
|
|
2
3
|
pollers::{self, Poller},
|
|
3
4
|
worker::client::WorkerClient,
|
|
4
5
|
};
|
|
5
6
|
use futures::{prelude::stream::FuturesUnordered, StreamExt};
|
|
7
|
+
use futures_util::{future::BoxFuture, FutureExt};
|
|
8
|
+
use governor::{Quota, RateLimiter};
|
|
6
9
|
use std::{
|
|
7
10
|
fmt::Debug,
|
|
8
11
|
future::Future,
|
|
9
12
|
sync::{
|
|
10
|
-
atomic::{AtomicUsize, Ordering},
|
|
13
|
+
atomic::{AtomicBool, AtomicUsize, Ordering},
|
|
11
14
|
Arc,
|
|
12
15
|
},
|
|
16
|
+
time::Duration,
|
|
13
17
|
};
|
|
14
|
-
use temporal_sdk_core_protos::temporal::api::
|
|
15
|
-
|
|
18
|
+
use temporal_sdk_core_protos::temporal::api::{
|
|
19
|
+
taskqueue::v1::TaskQueue,
|
|
20
|
+
workflowservice::v1::{PollActivityTaskQueueResponse, PollWorkflowTaskQueueResponse},
|
|
16
21
|
};
|
|
17
22
|
use tokio::{
|
|
18
23
|
sync::{
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
broadcast,
|
|
25
|
+
mpsc::{unbounded_channel, UnboundedReceiver},
|
|
26
|
+
Mutex,
|
|
21
27
|
},
|
|
22
28
|
task::JoinHandle,
|
|
23
29
|
};
|
|
24
30
|
use tokio_util::sync::CancellationToken;
|
|
25
31
|
|
|
26
32
|
pub struct LongPollBuffer<T> {
|
|
27
|
-
buffered_polls: Mutex<
|
|
33
|
+
buffered_polls: Mutex<UnboundedReceiver<pollers::Result<(T, OwnedMeteredSemPermit)>>>,
|
|
28
34
|
shutdown: CancellationToken,
|
|
29
|
-
/// This semaphore exists to ensure that we only poll server as many times as core actually
|
|
30
|
-
/// *asked* it to be polled - otherwise we might spin and buffer polls constantly. This also
|
|
31
|
-
/// means unit tests can continue to function in a predictable manner when calling mocks.
|
|
32
|
-
polls_requested: Arc<Semaphore>,
|
|
33
35
|
join_handles: FuturesUnordered<JoinHandle<()>>,
|
|
34
|
-
///
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
/// Pollers won't actually start polling until initialized & value is sent
|
|
37
|
+
starter: broadcast::Sender<()>,
|
|
38
|
+
did_start: AtomicBool,
|
|
37
39
|
}
|
|
38
40
|
|
|
39
|
-
struct ActiveCounter<'a>(&'a AtomicUsize);
|
|
40
|
-
impl<'a> ActiveCounter<'a>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
struct ActiveCounter<'a, F: Fn(usize)>(&'a AtomicUsize, Option<F>);
|
|
42
|
+
impl<'a, F> ActiveCounter<'a, F>
|
|
43
|
+
where
|
|
44
|
+
F: Fn(usize),
|
|
45
|
+
{
|
|
46
|
+
fn new(a: &'a AtomicUsize, change_fn: Option<F>) -> Self {
|
|
47
|
+
let v = a.fetch_add(1, Ordering::Relaxed) + 1;
|
|
48
|
+
if let Some(cfn) = change_fn.as_ref() {
|
|
49
|
+
cfn(v);
|
|
50
|
+
}
|
|
51
|
+
Self(a, change_fn)
|
|
44
52
|
}
|
|
45
53
|
}
|
|
46
|
-
impl Drop for ActiveCounter<'_>
|
|
54
|
+
impl<F> Drop for ActiveCounter<'_, F>
|
|
55
|
+
where
|
|
56
|
+
F: Fn(usize),
|
|
57
|
+
{
|
|
47
58
|
fn drop(&mut self) {
|
|
48
|
-
self.0.fetch_sub(1, Ordering::Relaxed);
|
|
59
|
+
let v = self.0.fetch_sub(1, Ordering::Relaxed) - 1;
|
|
60
|
+
if let Some(cfn) = self.1.as_ref() {
|
|
61
|
+
cfn(v)
|
|
62
|
+
}
|
|
49
63
|
}
|
|
50
64
|
}
|
|
51
65
|
|
|
@@ -53,42 +67,67 @@ impl<T> LongPollBuffer<T>
|
|
|
53
67
|
where
|
|
54
68
|
T: Send + Debug + 'static,
|
|
55
69
|
{
|
|
56
|
-
pub fn new<FT>(
|
|
70
|
+
pub(crate) fn new<FT, DelayFut>(
|
|
57
71
|
poll_fn: impl Fn() -> FT + Send + Sync + 'static,
|
|
72
|
+
poll_semaphore: Arc<MeteredSemaphore>,
|
|
58
73
|
max_pollers: usize,
|
|
59
|
-
buffer_size: usize,
|
|
60
74
|
shutdown: CancellationToken,
|
|
75
|
+
num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
|
|
76
|
+
pre_permit_delay: Option<impl Fn() -> DelayFut + Send + Sync + 'static>,
|
|
61
77
|
) -> Self
|
|
62
78
|
where
|
|
63
79
|
FT: Future<Output = pollers::Result<T>> + Send,
|
|
80
|
+
DelayFut: Future<Output = ()> + Send,
|
|
64
81
|
{
|
|
65
|
-
let (tx, rx) =
|
|
66
|
-
let
|
|
82
|
+
let (tx, rx) = unbounded_channel();
|
|
83
|
+
let (starter, wait_for_start) = broadcast::channel(1);
|
|
67
84
|
let active_pollers = Arc::new(AtomicUsize::new(0));
|
|
68
85
|
let join_handles = FuturesUnordered::new();
|
|
69
86
|
let pf = Arc::new(poll_fn);
|
|
87
|
+
let nph = num_pollers_handler.map(Arc::new);
|
|
88
|
+
let pre_permit_delay = pre_permit_delay.map(Arc::new);
|
|
70
89
|
for _ in 0..max_pollers {
|
|
71
90
|
let tx = tx.clone();
|
|
72
91
|
let pf = pf.clone();
|
|
73
92
|
let shutdown = shutdown.clone();
|
|
74
|
-
let polls_requested = polls_requested.clone();
|
|
75
93
|
let ap = active_pollers.clone();
|
|
94
|
+
let poll_semaphore = poll_semaphore.clone();
|
|
95
|
+
let nph = nph.clone();
|
|
96
|
+
let pre_permit_delay = pre_permit_delay.clone();
|
|
97
|
+
let mut wait_for_start = wait_for_start.resubscribe();
|
|
76
98
|
let jh = tokio::spawn(async move {
|
|
99
|
+
tokio::select! {
|
|
100
|
+
_ = wait_for_start.recv() => (),
|
|
101
|
+
_ = shutdown.cancelled() => return,
|
|
102
|
+
}
|
|
103
|
+
drop(wait_for_start);
|
|
104
|
+
|
|
105
|
+
let nph = nph.as_ref().map(|a| a.as_ref());
|
|
77
106
|
loop {
|
|
78
107
|
if shutdown.is_cancelled() {
|
|
79
108
|
break;
|
|
80
109
|
}
|
|
81
|
-
let
|
|
82
|
-
|
|
83
|
-
|
|
110
|
+
if let Some(ref ppd) = pre_permit_delay {
|
|
111
|
+
tokio::select! {
|
|
112
|
+
_ = ppd() => (),
|
|
113
|
+
_ = shutdown.cancelled() => break,
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
let permit = tokio::select! {
|
|
117
|
+
p = poll_semaphore.acquire_owned() => p,
|
|
118
|
+
_ = shutdown.cancelled() => break,
|
|
119
|
+
};
|
|
120
|
+
let permit = if let Ok(p) = permit {
|
|
121
|
+
p
|
|
122
|
+
} else {
|
|
123
|
+
break;
|
|
84
124
|
};
|
|
85
|
-
let _active_guard = ActiveCounter::new(ap.as_ref());
|
|
125
|
+
let _active_guard = ActiveCounter::new(ap.as_ref(), nph);
|
|
86
126
|
let r = tokio::select! {
|
|
87
127
|
r = pf() => r,
|
|
88
|
-
_ = shutdown.cancelled() =>
|
|
128
|
+
_ = shutdown.cancelled() => break,
|
|
89
129
|
};
|
|
90
|
-
|
|
91
|
-
let _ = tx.send(r).await;
|
|
130
|
+
let _ = tx.send(r.map(|r| (r, permit)));
|
|
92
131
|
}
|
|
93
132
|
});
|
|
94
133
|
join_handles.push(jh);
|
|
@@ -96,50 +135,29 @@ where
|
|
|
96
135
|
Self {
|
|
97
136
|
buffered_polls: Mutex::new(rx),
|
|
98
137
|
shutdown,
|
|
99
|
-
polls_requested,
|
|
100
138
|
join_handles,
|
|
101
|
-
|
|
102
|
-
|
|
139
|
+
starter,
|
|
140
|
+
did_start: AtomicBool::new(false),
|
|
103
141
|
}
|
|
104
142
|
}
|
|
105
|
-
|
|
106
|
-
/// Set a function that will be called every time the number of pollers changes.
|
|
107
|
-
/// TODO: Currently a bit weird, will make more sense once we implement dynamic poller scaling.
|
|
108
|
-
pub fn set_num_pollers_handler(&mut self, handler: impl Fn(usize) + Send + Sync + 'static) {
|
|
109
|
-
self.num_pollers_changed = Some(Box::new(handler));
|
|
110
|
-
}
|
|
111
143
|
}
|
|
112
144
|
|
|
113
145
|
#[async_trait::async_trait]
|
|
114
|
-
impl<T> Poller<T> for LongPollBuffer<T>
|
|
146
|
+
impl<T> Poller<(T, OwnedMeteredSemPermit)> for LongPollBuffer<T>
|
|
115
147
|
where
|
|
116
148
|
T: Send + Sync + Debug + 'static,
|
|
117
149
|
{
|
|
118
|
-
/// Poll
|
|
119
|
-
/// buffer may support many concurrent pollers, but there is no reason to have them poll unless
|
|
120
|
-
/// enough polls have actually been requested. Calling this function adds a permit that any
|
|
121
|
-
/// concurrent poller may fulfill.
|
|
122
|
-
///
|
|
123
|
-
/// EX: If this function is only ever called serially and always `await`ed, there will be no
|
|
124
|
-
/// concurrent polling. If it is called many times and the futures are awaited concurrently,
|
|
125
|
-
/// then polling will happen concurrently.
|
|
150
|
+
/// Poll for the next item from this poller
|
|
126
151
|
///
|
|
127
|
-
/// Returns `None` if the
|
|
152
|
+
/// Returns `None` if the poller has been shut down
|
|
128
153
|
#[instrument(name = "long_poll", level = "trace", skip(self))]
|
|
129
|
-
async fn poll(&self) -> Option<pollers::Result<T>> {
|
|
130
|
-
self.
|
|
131
|
-
|
|
132
|
-
fun(self.active_pollers.load(Ordering::Relaxed));
|
|
154
|
+
async fn poll(&self) -> Option<pollers::Result<(T, OwnedMeteredSemPermit)>> {
|
|
155
|
+
if !self.did_start.fetch_or(true, Ordering::Relaxed) {
|
|
156
|
+
let _ = self.starter.send(());
|
|
133
157
|
}
|
|
134
158
|
|
|
135
159
|
let mut locked = self.buffered_polls.lock().await;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if let Some(fun) = self.num_pollers_changed.as_ref() {
|
|
139
|
-
fun(self.active_pollers.load(Ordering::Relaxed));
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
res
|
|
160
|
+
(*locked).recv().await
|
|
143
161
|
}
|
|
144
162
|
|
|
145
163
|
fn notify_shutdown(&self) {
|
|
@@ -148,7 +166,13 @@ where
|
|
|
148
166
|
|
|
149
167
|
async fn shutdown(mut self) {
|
|
150
168
|
self.notify_shutdown();
|
|
151
|
-
while self.join_handles.next().await
|
|
169
|
+
while let Some(jh) = self.join_handles.next().await {
|
|
170
|
+
if let Err(e) = jh {
|
|
171
|
+
if !e.is_cancelled() {
|
|
172
|
+
dbg_panic!("Poller task did not terminate cleanly: {:?}", e);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
152
176
|
}
|
|
153
177
|
|
|
154
178
|
async fn shutdown_box(self: Box<Self>) {
|
|
@@ -165,8 +189,10 @@ pub struct WorkflowTaskPoller {
|
|
|
165
189
|
}
|
|
166
190
|
|
|
167
191
|
#[async_trait::async_trait]
|
|
168
|
-
impl Poller<PollWorkflowTaskQueueResponse> for WorkflowTaskPoller {
|
|
169
|
-
async fn poll(
|
|
192
|
+
impl Poller<(PollWorkflowTaskQueueResponse, OwnedMeteredSemPermit)> for WorkflowTaskPoller {
|
|
193
|
+
async fn poll(
|
|
194
|
+
&self,
|
|
195
|
+
) -> Option<pollers::Result<(PollWorkflowTaskQueueResponse, OwnedMeteredSemPermit)>> {
|
|
170
196
|
if let Some(sq) = self.sticky_poller.as_ref() {
|
|
171
197
|
tokio::select! {
|
|
172
198
|
r = self.normal_poller.poll() => r,
|
|
@@ -200,51 +226,106 @@ impl Poller<PollWorkflowTaskQueueResponse> for WorkflowTaskPoller {
|
|
|
200
226
|
pub type PollWorkflowTaskBuffer = LongPollBuffer<PollWorkflowTaskQueueResponse>;
|
|
201
227
|
pub(crate) fn new_workflow_task_buffer(
|
|
202
228
|
client: Arc<dyn WorkerClient>,
|
|
203
|
-
task_queue:
|
|
204
|
-
is_sticky: bool,
|
|
229
|
+
task_queue: TaskQueue,
|
|
205
230
|
concurrent_pollers: usize,
|
|
206
|
-
|
|
231
|
+
semaphore: Arc<MeteredSemaphore>,
|
|
207
232
|
shutdown: CancellationToken,
|
|
233
|
+
num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
|
|
208
234
|
) -> PollWorkflowTaskBuffer {
|
|
209
235
|
LongPollBuffer::new(
|
|
210
236
|
move || {
|
|
211
237
|
let client = client.clone();
|
|
212
238
|
let task_queue = task_queue.clone();
|
|
213
|
-
async move { client.poll_workflow_task(task_queue
|
|
239
|
+
async move { client.poll_workflow_task(task_queue).await }
|
|
214
240
|
},
|
|
241
|
+
semaphore,
|
|
215
242
|
concurrent_pollers,
|
|
216
|
-
buffer_size,
|
|
217
243
|
shutdown,
|
|
244
|
+
num_pollers_handler,
|
|
245
|
+
None::<fn() -> BoxFuture<'static, ()>>,
|
|
218
246
|
)
|
|
219
247
|
}
|
|
220
248
|
|
|
221
249
|
pub type PollActivityTaskBuffer = LongPollBuffer<PollActivityTaskQueueResponse>;
|
|
250
|
+
#[allow(clippy::too_many_arguments)]
|
|
222
251
|
pub(crate) fn new_activity_task_buffer(
|
|
223
252
|
client: Arc<dyn WorkerClient>,
|
|
224
253
|
task_queue: String,
|
|
225
254
|
concurrent_pollers: usize,
|
|
226
|
-
|
|
255
|
+
semaphore: Arc<MeteredSemaphore>,
|
|
227
256
|
max_tps: Option<f64>,
|
|
228
257
|
shutdown: CancellationToken,
|
|
258
|
+
num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
|
|
259
|
+
max_worker_acts_per_sec: Option<f64>,
|
|
229
260
|
) -> PollActivityTaskBuffer {
|
|
261
|
+
let rate_limiter = max_worker_acts_per_sec.and_then(|ps| {
|
|
262
|
+
Quota::with_period(Duration::from_secs_f64(ps.recip()))
|
|
263
|
+
.map(|q| Arc::new(RateLimiter::direct(q)))
|
|
264
|
+
});
|
|
230
265
|
LongPollBuffer::new(
|
|
231
266
|
move || {
|
|
232
267
|
let client = client.clone();
|
|
233
268
|
let task_queue = task_queue.clone();
|
|
234
269
|
async move { client.poll_activity_task(task_queue, max_tps).await }
|
|
235
270
|
},
|
|
271
|
+
semaphore,
|
|
236
272
|
concurrent_pollers,
|
|
237
|
-
buffer_size,
|
|
238
273
|
shutdown,
|
|
274
|
+
num_pollers_handler,
|
|
275
|
+
rate_limiter.map(|rl| {
|
|
276
|
+
move || {
|
|
277
|
+
let rl = rl.clone();
|
|
278
|
+
async move { rl.until_ready().await }.boxed()
|
|
279
|
+
}
|
|
280
|
+
}),
|
|
239
281
|
)
|
|
240
282
|
}
|
|
241
283
|
|
|
284
|
+
#[cfg(test)]
|
|
285
|
+
#[derive(derive_more::Constructor)]
|
|
286
|
+
pub(crate) struct MockPermittedPollBuffer<PT> {
|
|
287
|
+
sem: Arc<MeteredSemaphore>,
|
|
288
|
+
inner: PT,
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
#[cfg(test)]
|
|
292
|
+
#[async_trait::async_trait]
|
|
293
|
+
impl<T, PT> Poller<(T, OwnedMeteredSemPermit)> for MockPermittedPollBuffer<PT>
|
|
294
|
+
where
|
|
295
|
+
T: Send + Sync + 'static,
|
|
296
|
+
PT: Poller<T> + Send + Sync + 'static,
|
|
297
|
+
{
|
|
298
|
+
async fn poll(&self) -> Option<pollers::Result<(T, OwnedMeteredSemPermit)>> {
|
|
299
|
+
let p = self
|
|
300
|
+
.sem
|
|
301
|
+
.acquire_owned()
|
|
302
|
+
.await
|
|
303
|
+
.expect("Semaphore in poller not closed!");
|
|
304
|
+
self.inner.poll().await.map(|r| r.map(|r| (r, p)))
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
fn notify_shutdown(&self) {
|
|
308
|
+
self.inner.notify_shutdown();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async fn shutdown(self) {
|
|
312
|
+
self.inner.shutdown().await;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async fn shutdown_box(self: Box<Self>) {
|
|
316
|
+
self.inner.shutdown().await;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
242
320
|
#[cfg(test)]
|
|
243
321
|
mod tests {
|
|
244
322
|
use super::*;
|
|
245
|
-
use crate::
|
|
323
|
+
use crate::{
|
|
324
|
+
telemetry::metrics::MetricsContext, worker::client::mocks::mock_manual_workflow_client,
|
|
325
|
+
};
|
|
246
326
|
use futures::FutureExt;
|
|
247
327
|
use std::time::Duration;
|
|
328
|
+
use temporal_sdk_core_protos::temporal::api::enums::v1::TaskQueueKind;
|
|
248
329
|
use tokio::{select, sync::mpsc::channel};
|
|
249
330
|
|
|
250
331
|
#[tokio::test]
|
|
@@ -253,7 +334,7 @@ mod tests {
|
|
|
253
334
|
mock_client
|
|
254
335
|
.expect_poll_workflow_task()
|
|
255
336
|
.times(2)
|
|
256
|
-
.returning(move |_
|
|
337
|
+
.returning(move |_| {
|
|
257
338
|
async {
|
|
258
339
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
|
259
340
|
Ok(Default::default())
|
|
@@ -263,11 +344,19 @@ mod tests {
|
|
|
263
344
|
|
|
264
345
|
let pb = new_workflow_task_buffer(
|
|
265
346
|
Arc::new(mock_client),
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
347
|
+
TaskQueue {
|
|
348
|
+
name: "sometq".to_string(),
|
|
349
|
+
kind: TaskQueueKind::Normal as i32,
|
|
350
|
+
normal_name: "".to_string(),
|
|
351
|
+
},
|
|
269
352
|
1,
|
|
353
|
+
Arc::new(MeteredSemaphore::new(
|
|
354
|
+
10,
|
|
355
|
+
MetricsContext::no_op(),
|
|
356
|
+
|_, _| {},
|
|
357
|
+
)),
|
|
270
358
|
CancellationToken::new(),
|
|
359
|
+
None::<fn(usize)>,
|
|
271
360
|
);
|
|
272
361
|
|
|
273
362
|
// Poll a bunch of times, "interrupting" it each time, we should only actually have polled
|
|
@@ -337,10 +337,7 @@ impl Default for LACloseTimeouts {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
impl ValidScheduleLA {
|
|
340
|
-
pub fn from_schedule_la(
|
|
341
|
-
v: ScheduleLocalActivity,
|
|
342
|
-
wf_exe_timeout: Option<Duration>,
|
|
343
|
-
) -> Result<Self, anyhow::Error> {
|
|
340
|
+
pub fn from_schedule_la(v: ScheduleLocalActivity) -> Result<Self, anyhow::Error> {
|
|
344
341
|
let original_schedule_time = v
|
|
345
342
|
.original_schedule_time
|
|
346
343
|
.map(|x| {
|
|
@@ -354,9 +351,7 @@ impl ValidScheduleLA {
|
|
|
354
351
|
x.try_into()
|
|
355
352
|
.map_err(|_| anyhow!("Could not convert schedule_to_close_timeout"))
|
|
356
353
|
})
|
|
357
|
-
.transpose()
|
|
358
|
-
// Default to execution timeout if unset
|
|
359
|
-
.or(wf_exe_timeout);
|
|
354
|
+
.transpose()?;
|
|
360
355
|
let mut schedule_to_start_timeout = v
|
|
361
356
|
.schedule_to_start_timeout
|
|
362
357
|
.map(|x| {
|
|
@@ -392,7 +387,8 @@ impl ValidScheduleLA {
|
|
|
392
387
|
}
|
|
393
388
|
(None, None) => {
|
|
394
389
|
return Err(anyhow!(
|
|
395
|
-
"One of schedule_to_close or start_to_close timeouts must be set
|
|
390
|
+
"One or both of schedule_to_close or start_to_close timeouts must be set for \
|
|
391
|
+
local activities"
|
|
396
392
|
))
|
|
397
393
|
}
|
|
398
394
|
};
|
|
@@ -89,7 +89,7 @@ pub(crate) fn mock_client_from_histories(historator: Historator) -> impl WorkerC
|
|
|
89
89
|
let hist_allow_tx = historator.replay_done_tx.clone();
|
|
90
90
|
let historator = Arc::new(TokioMutex::new(historator));
|
|
91
91
|
|
|
92
|
-
mg.expect_poll_workflow_task().returning(move |_
|
|
92
|
+
mg.expect_poll_workflow_task().returning(move |_| {
|
|
93
93
|
let historator = historator.clone();
|
|
94
94
|
async move {
|
|
95
95
|
let mut hlock = historator.lock().await;
|
|
@@ -87,6 +87,7 @@ struct Instruments {
|
|
|
87
87
|
sticky_cache_hit: Counter<u64>,
|
|
88
88
|
sticky_cache_miss: Counter<u64>,
|
|
89
89
|
sticky_cache_size: Histogram<u64>,
|
|
90
|
+
sticky_cache_evictions: Counter<u64>,
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
impl MetricsContext {
|
|
@@ -288,6 +289,13 @@ impl MetricsContext {
|
|
|
288
289
|
.sticky_cache_size
|
|
289
290
|
.record(&self.ctx, size, &self.kvs);
|
|
290
291
|
}
|
|
292
|
+
|
|
293
|
+
/// Count a workflow being evicted from the cache
|
|
294
|
+
pub(crate) fn cache_eviction(&self) {
|
|
295
|
+
self.instruments
|
|
296
|
+
.sticky_cache_evictions
|
|
297
|
+
.add(&self.ctx, 1, &self.kvs);
|
|
298
|
+
}
|
|
291
299
|
}
|
|
292
300
|
|
|
293
301
|
impl Instruments {
|
|
@@ -327,6 +335,7 @@ impl Instruments {
|
|
|
327
335
|
sticky_cache_hit: meter.counter("sticky_cache_hit"),
|
|
328
336
|
sticky_cache_miss: meter.counter("sticky_cache_miss"),
|
|
329
337
|
sticky_cache_size: meter.histogram(STICKY_CACHE_SIZE_NAME),
|
|
338
|
+
sticky_cache_evictions: meter.counter("sticky_cache_total_forced_eviction"),
|
|
330
339
|
}
|
|
331
340
|
}
|
|
332
341
|
}
|
|
@@ -297,6 +297,9 @@ pub fn telemetry_init(opts: TelemetryOptions) -> Result<TelemetryInstance, anyho
|
|
|
297
297
|
.with(forward_layer)
|
|
298
298
|
.with(export_layer);
|
|
299
299
|
|
|
300
|
+
#[cfg(feature = "tokio-console")]
|
|
301
|
+
let reg = reg.with(console_subscriber::spawn());
|
|
302
|
+
|
|
300
303
|
tx.send(TelemetryInstance::new(
|
|
301
304
|
Arc::new(reg),
|
|
302
305
|
logs_out,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
pub(crate) use temporal_sdk_core_test_utils::canned_histories;
|
|
2
2
|
|
|
3
3
|
use crate::{
|
|
4
|
-
pollers::{
|
|
4
|
+
pollers::{BoxedPoller, MockManualPoller, MockPoller},
|
|
5
5
|
protosext::ValidPollWFTQResponse,
|
|
6
6
|
replay::TestHistoryBuilder,
|
|
7
7
|
sticky_q_name_for_worker,
|
|
@@ -10,7 +10,7 @@ use crate::{
|
|
|
10
10
|
client::{
|
|
11
11
|
mocks::mock_workflow_client, MockWorkerClient, WorkerClient, WorkflowTaskCompletion,
|
|
12
12
|
},
|
|
13
|
-
|
|
13
|
+
TaskPollers,
|
|
14
14
|
},
|
|
15
15
|
TaskToken, Worker, WorkerConfig, WorkerConfigBuilder,
|
|
16
16
|
};
|
|
@@ -53,7 +53,6 @@ use temporal_sdk_core_protos::{
|
|
|
53
53
|
use temporal_sdk_core_test_utils::TestWorker;
|
|
54
54
|
use tokio::sync::{mpsc::unbounded_channel, Notify};
|
|
55
55
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
56
|
-
use tokio_util::sync::CancellationToken;
|
|
57
56
|
|
|
58
57
|
pub const TEST_Q: &str = "q";
|
|
59
58
|
|
|
@@ -148,11 +147,12 @@ pub(crate) fn mock_worker(mocks: MocksHolder) -> Worker {
|
|
|
148
147
|
mocks.inputs.config,
|
|
149
148
|
sticky_q,
|
|
150
149
|
mocks.client,
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
TaskPollers::Mocked {
|
|
151
|
+
wft_stream: mocks.inputs.wft_stream,
|
|
152
|
+
act_poller,
|
|
153
|
+
},
|
|
153
154
|
MetricsContext::no_op(),
|
|
154
155
|
None,
|
|
155
|
-
CancellationToken::new(),
|
|
156
156
|
)
|
|
157
157
|
}
|
|
158
158
|
|
|
@@ -187,7 +187,7 @@ impl MocksHolder {
|
|
|
187
187
|
pub fn worker_cfg(&mut self, mutator: impl FnOnce(&mut WorkerConfig)) {
|
|
188
188
|
mutator(&mut self.inputs.config);
|
|
189
189
|
}
|
|
190
|
-
pub fn set_act_poller(&mut self, poller:
|
|
190
|
+
pub fn set_act_poller(&mut self, poller: BoxedPoller<PollActivityTaskQueueResponse>) {
|
|
191
191
|
self.inputs.act_poller = Some(poller);
|
|
192
192
|
}
|
|
193
193
|
/// Can be used for tests that need to avoid auto-shutdown due to running out of mock responses
|
|
@@ -199,13 +199,13 @@ impl MocksHolder {
|
|
|
199
199
|
|
|
200
200
|
pub struct MockWorkerInputs {
|
|
201
201
|
pub wft_stream: BoxStream<'static, Result<ValidPollWFTQResponse, tonic::Status>>,
|
|
202
|
-
pub act_poller: Option<
|
|
202
|
+
pub act_poller: Option<BoxedPoller<PollActivityTaskQueueResponse>>,
|
|
203
203
|
pub config: WorkerConfig,
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
impl Default for MockWorkerInputs {
|
|
207
207
|
fn default() -> Self {
|
|
208
|
-
Self::
|
|
208
|
+
Self::new(stream::empty().boxed())
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
@@ -219,13 +219,6 @@ impl MockWorkerInputs {
|
|
|
219
219
|
config: test_worker_cfg().build().unwrap(),
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
|
-
pub fn new_from_poller(wf_poller: BoxedWFPoller) -> Self {
|
|
223
|
-
Self {
|
|
224
|
-
wft_stream: new_wft_poller(wf_poller, MetricsContext::no_op()).boxed(),
|
|
225
|
-
act_poller: None,
|
|
226
|
-
config: test_worker_cfg().build().unwrap(),
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
222
|
}
|
|
230
223
|
|
|
231
224
|
impl MocksHolder {
|
|
@@ -1,23 +1,11 @@
|
|
|
1
|
-
use crate::{
|
|
2
|
-
abstractions::MeteredSemaphore, pollers::BoxedActPoller, worker::activities::PermittedTqResp,
|
|
3
|
-
MetricsContext,
|
|
4
|
-
};
|
|
1
|
+
use crate::{pollers::BoxedActPoller, worker::activities::PermittedTqResp, MetricsContext};
|
|
5
2
|
use futures::{stream, Stream};
|
|
6
|
-
use governor::{
|
|
7
|
-
clock::DefaultClock,
|
|
8
|
-
middleware::NoOpMiddleware,
|
|
9
|
-
state::{InMemoryState, NotKeyed},
|
|
10
|
-
RateLimiter,
|
|
11
|
-
};
|
|
12
|
-
use std::sync::Arc;
|
|
13
3
|
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
|
|
14
4
|
use tokio::select;
|
|
15
5
|
use tokio_util::sync::CancellationToken;
|
|
16
6
|
|
|
17
7
|
struct StreamState {
|
|
18
8
|
poller: BoxedActPoller,
|
|
19
|
-
semaphore: Arc<MeteredSemaphore>,
|
|
20
|
-
rate_limiter: Option<RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>>,
|
|
21
9
|
metrics: MetricsContext,
|
|
22
10
|
shutdown_token: CancellationToken,
|
|
23
11
|
poller_was_shutdown: bool,
|
|
@@ -25,15 +13,11 @@ struct StreamState {
|
|
|
25
13
|
|
|
26
14
|
pub(crate) fn new_activity_task_poller(
|
|
27
15
|
poller: BoxedActPoller,
|
|
28
|
-
semaphore: Arc<MeteredSemaphore>,
|
|
29
|
-
rate_limiter: Option<RateLimiter<NotKeyed, InMemoryState, DefaultClock, NoOpMiddleware>>,
|
|
30
16
|
metrics: MetricsContext,
|
|
31
17
|
shutdown_token: CancellationToken,
|
|
32
18
|
) -> impl Stream<Item = Result<PermittedTqResp, tonic::Status>> {
|
|
33
19
|
let state = StreamState {
|
|
34
20
|
poller,
|
|
35
|
-
semaphore,
|
|
36
|
-
rate_limiter,
|
|
37
21
|
metrics,
|
|
38
22
|
shutdown_token,
|
|
39
23
|
poller_was_shutdown: false,
|
|
@@ -41,21 +25,12 @@ pub(crate) fn new_activity_task_poller(
|
|
|
41
25
|
stream::unfold(state, |mut state| async move {
|
|
42
26
|
loop {
|
|
43
27
|
let poll = async {
|
|
44
|
-
let permit = state
|
|
45
|
-
.semaphore
|
|
46
|
-
.acquire_owned()
|
|
47
|
-
.await
|
|
48
|
-
.expect("outstanding activity semaphore not closed");
|
|
49
|
-
if !state.poller_was_shutdown {
|
|
50
|
-
if let Some(ref rl) = state.rate_limiter {
|
|
51
|
-
rl.until_ready().await;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
28
|
loop {
|
|
55
29
|
return match state.poller.poll().await {
|
|
56
|
-
Some(Ok(resp)) => {
|
|
30
|
+
Some(Ok((resp, permit))) => {
|
|
57
31
|
if resp == PollActivityTaskQueueResponse::default() {
|
|
58
|
-
// We get the default proto in the event that the long poll times
|
|
32
|
+
// We get the default proto in the event that the long poll times
|
|
33
|
+
// out.
|
|
59
34
|
debug!("Poll activity task timeout");
|
|
60
35
|
state.metrics.act_poll_timeout();
|
|
61
36
|
continue;
|
|
@@ -66,8 +41,8 @@ pub(crate) fn new_activity_task_poller(
|
|
|
66
41
|
warn!(error=?e, "Error while polling for activity tasks");
|
|
67
42
|
Some(Err(e))
|
|
68
43
|
}
|
|
69
|
-
// If poller returns None, it's dead, thus we also return None to terminate
|
|
70
|
-
// stream.
|
|
44
|
+
// If poller returns None, it's dead, thus we also return None to terminate
|
|
45
|
+
// this stream.
|
|
71
46
|
None => None,
|
|
72
47
|
};
|
|
73
48
|
}
|