@temporalio/core-bridge 1.6.0 → 1.7.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 +520 -456
- package/lib/index.d.ts +8 -6
- package/lib/index.js.map +1 -1
- package/package.json +8 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.buildkite/docker/Dockerfile +2 -2
- package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
- package/sdk-core/.buildkite/pipeline.yml +1 -1
- package/sdk-core/.github/workflows/heavy.yml +1 -0
- package/sdk-core/README.md +13 -7
- package/sdk-core/client/src/lib.rs +27 -9
- package/sdk-core/client/src/metrics.rs +17 -8
- package/sdk-core/client/src/raw.rs +3 -3
- package/sdk-core/core/Cargo.toml +3 -4
- package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
- package/sdk-core/core/src/abstractions.rs +197 -18
- package/sdk-core/core/src/core_tests/activity_tasks.rs +137 -45
- package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
- package/sdk-core/core/src/core_tests/determinism.rs +212 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +183 -36
- package/sdk-core/core/src/core_tests/queries.rs +32 -14
- package/sdk-core/core/src/core_tests/workers.rs +8 -5
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +340 -51
- package/sdk-core/core/src/ephemeral_server/mod.rs +110 -8
- package/sdk-core/core/src/internal_flags.rs +141 -0
- package/sdk-core/core/src/lib.rs +14 -9
- package/sdk-core/core/src/replay/mod.rs +16 -27
- package/sdk-core/core/src/telemetry/metrics.rs +69 -35
- package/sdk-core/core/src/telemetry/mod.rs +38 -14
- package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
- package/sdk-core/core/src/test_help/mod.rs +65 -13
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
- package/sdk-core/core/src/worker/activities/local_activities.rs +122 -6
- package/sdk-core/core/src/worker/activities.rs +347 -173
- package/sdk-core/core/src/worker/client/mocks.rs +22 -2
- package/sdk-core/core/src/worker/client.rs +18 -2
- package/sdk-core/core/src/worker/mod.rs +137 -44
- package/sdk-core/core/src/worker/workflow/history_update.rs +132 -51
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +207 -166
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +6 -7
- package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -7
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +157 -82
- package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +12 -12
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +6 -7
- package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +13 -15
- package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +170 -60
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +24 -16
- package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +6 -8
- package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +320 -204
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +10 -13
- package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +15 -23
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +187 -46
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +237 -111
- package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +13 -13
- package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +10 -6
- package/sdk-core/core/src/worker/workflow/managed_run.rs +81 -62
- package/sdk-core/core/src/worker/workflow/mod.rs +341 -79
- package/sdk-core/core/src/worker/workflow/run_cache.rs +18 -11
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +15 -3
- package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -0
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +75 -52
- package/sdk-core/core-api/Cargo.toml +0 -1
- package/sdk-core/core-api/src/lib.rs +13 -7
- package/sdk-core/core-api/src/telemetry.rs +4 -6
- package/sdk-core/core-api/src/worker.rs +5 -0
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +80 -55
- package/sdk-core/fsm/rustfsm_trait/src/lib.rs +22 -68
- package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
- package/sdk-core/histories/old_change_marker_format.bin +0 -0
- package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
- package/sdk-core/protos/api_upstream/Makefile +1 -1
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
- package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
- package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
- package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
- package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
- package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
- package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
- package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
- package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
- package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +65 -60
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
- package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
- package/sdk-core/sdk/Cargo.toml +1 -1
- package/sdk-core/sdk/src/lib.rs +21 -5
- package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
- package/sdk-core/sdk/src/workflow_context.rs +24 -17
- package/sdk-core/sdk/src/workflow_future.rs +9 -3
- package/sdk-core/sdk-core-protos/src/history_builder.rs +114 -89
- package/sdk-core/sdk-core-protos/src/history_info.rs +6 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +205 -64
- package/sdk-core/test-utils/src/canned_histories.rs +106 -296
- package/sdk-core/test-utils/src/lib.rs +32 -5
- package/sdk-core/tests/heavy_tests.rs +10 -43
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
- package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
- package/sdk-core/tests/integ_tests/polling_tests.rs +3 -8
- package/sdk-core/tests/integ_tests/queries_tests.rs +4 -2
- package/sdk-core/tests/integ_tests/visibility_tests.rs +34 -23
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +97 -81
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +5 -1
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +25 -3
- package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +30 -0
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +64 -0
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +4 -0
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +3 -1
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +7 -2
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -7
- package/sdk-core/tests/integ_tests/workflow_tests.rs +8 -8
- package/sdk-core/tests/main.rs +16 -25
- package/sdk-core/tests/runner.rs +11 -9
- package/src/conversions.rs +14 -8
- package/src/runtime.rs +9 -8
- package/ts/index.ts +8 -6
- package/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
//! This module contains very generic helpers that can be used codebase-wide
|
|
2
2
|
|
|
3
|
+
pub mod take_cell;
|
|
4
|
+
|
|
3
5
|
use crate::MetricsContext;
|
|
6
|
+
use derive_more::DebugCustom;
|
|
4
7
|
use futures::{stream, Stream, StreamExt};
|
|
5
8
|
use std::{
|
|
6
9
|
fmt::{Debug, Formatter},
|
|
7
|
-
sync::
|
|
10
|
+
sync::{
|
|
11
|
+
atomic::{AtomicBool, AtomicUsize, Ordering},
|
|
12
|
+
Arc,
|
|
13
|
+
},
|
|
8
14
|
};
|
|
9
15
|
use tokio::sync::{AcquireError, OwnedSemaphorePermit, Semaphore, TryAcquireError};
|
|
16
|
+
use tokio_util::sync::CancellationToken;
|
|
10
17
|
|
|
11
18
|
/// Wraps a [Semaphore] with a function call that is fed the available permits any time a permit is
|
|
12
19
|
/// acquired or restored through the provided methods
|
|
13
20
|
#[derive(Clone)]
|
|
14
21
|
pub(crate) struct MeteredSemaphore {
|
|
15
22
|
sem: Arc<Semaphore>,
|
|
23
|
+
/// The number of permit owners who have acquired a permit from the semaphore, but are not yet
|
|
24
|
+
/// meaningfully using that permit. This is useful for giving a more semantically accurate count
|
|
25
|
+
/// of used task slots, since we typically wait for a permit first before polling, but that slot
|
|
26
|
+
/// isn't used in the sense the user expects until we actually also get the corresponding task.
|
|
27
|
+
unused_claimants: Arc<AtomicUsize>,
|
|
16
28
|
metrics_ctx: MetricsContext,
|
|
17
29
|
record_fn: fn(&MetricsContext, usize),
|
|
18
30
|
}
|
|
@@ -25,6 +37,7 @@ impl MeteredSemaphore {
|
|
|
25
37
|
) -> Self {
|
|
26
38
|
Self {
|
|
27
39
|
sem: Arc::new(Semaphore::new(inital_permits)),
|
|
40
|
+
unused_claimants: Arc::new(AtomicUsize::new(0)),
|
|
28
41
|
metrics_ctx,
|
|
29
42
|
record_fn,
|
|
30
43
|
}
|
|
@@ -36,42 +49,154 @@ impl MeteredSemaphore {
|
|
|
36
49
|
|
|
37
50
|
pub async fn acquire_owned(&self) -> Result<OwnedMeteredSemPermit, AcquireError> {
|
|
38
51
|
let res = self.sem.clone().acquire_owned().await?;
|
|
39
|
-
self.
|
|
40
|
-
Ok(OwnedMeteredSemPermit {
|
|
41
|
-
inner: res,
|
|
42
|
-
record_fn: self.record_drop_owned(),
|
|
43
|
-
})
|
|
52
|
+
Ok(self.build_owned(res))
|
|
44
53
|
}
|
|
45
54
|
|
|
46
55
|
pub fn try_acquire_owned(&self) -> Result<OwnedMeteredSemPermit, TryAcquireError> {
|
|
47
56
|
let res = self.sem.clone().try_acquire_owned()?;
|
|
57
|
+
Ok(self.build_owned(res))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn build_owned(&self, res: OwnedSemaphorePermit) -> OwnedMeteredSemPermit {
|
|
61
|
+
self.unused_claimants.fetch_add(1, Ordering::Release);
|
|
48
62
|
self.record();
|
|
49
|
-
|
|
63
|
+
OwnedMeteredSemPermit {
|
|
50
64
|
inner: res,
|
|
51
|
-
|
|
52
|
-
|
|
65
|
+
unused_claimants: Some(self.unused_claimants.clone()),
|
|
66
|
+
record_fn: self.record_owned(),
|
|
67
|
+
}
|
|
53
68
|
}
|
|
54
69
|
|
|
55
70
|
fn record(&self) {
|
|
56
|
-
(self.record_fn)(
|
|
71
|
+
(self.record_fn)(
|
|
72
|
+
&self.metrics_ctx,
|
|
73
|
+
self.sem.available_permits() + self.unused_claimants.load(Ordering::Acquire),
|
|
74
|
+
);
|
|
57
75
|
}
|
|
58
76
|
|
|
59
|
-
fn
|
|
77
|
+
fn record_owned(&self) -> Box<dyn Fn(bool) + Send + Sync> {
|
|
60
78
|
let rcf = self.record_fn;
|
|
61
79
|
let mets = self.metrics_ctx.clone();
|
|
62
80
|
let sem = self.sem.clone();
|
|
63
|
-
|
|
81
|
+
let uc = self.unused_claimants.clone();
|
|
82
|
+
// When being called from the drop impl, the semaphore permit isn't actually dropped yet,
|
|
83
|
+
// so account for that.
|
|
84
|
+
Box::new(move |add_one: bool| {
|
|
85
|
+
let extra = usize::from(add_one);
|
|
86
|
+
rcf(
|
|
87
|
+
&mets,
|
|
88
|
+
sem.available_permits() + uc.load(Ordering::Acquire) + extra,
|
|
89
|
+
)
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// A version of [MeteredSemaphore] that can be closed and supports waiting for close to complete.
|
|
95
|
+
/// Once closed, no permits will be handed out.
|
|
96
|
+
/// Close completes when all permits have been returned.
|
|
97
|
+
pub(crate) struct ClosableMeteredSemaphore {
|
|
98
|
+
inner: Arc<MeteredSemaphore>,
|
|
99
|
+
outstanding_permits: AtomicUsize,
|
|
100
|
+
close_requested: AtomicBool,
|
|
101
|
+
close_complete_token: CancellationToken,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
impl ClosableMeteredSemaphore {
|
|
105
|
+
pub fn new_arc(sem: Arc<MeteredSemaphore>) -> Arc<Self> {
|
|
106
|
+
Arc::new(Self {
|
|
107
|
+
inner: sem,
|
|
108
|
+
outstanding_permits: Default::default(),
|
|
109
|
+
close_requested: AtomicBool::new(false),
|
|
110
|
+
close_complete_token: CancellationToken::new(),
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
impl ClosableMeteredSemaphore {
|
|
116
|
+
#[cfg(test)]
|
|
117
|
+
pub fn available_permits(&self) -> usize {
|
|
118
|
+
self.inner.available_permits()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Request to close the semaphore and prevent new permits from being acquired.
|
|
122
|
+
pub fn close(&self) {
|
|
123
|
+
self.close_requested.store(true, Ordering::Release);
|
|
124
|
+
if self.outstanding_permits.load(Ordering::Acquire) == 0 {
|
|
125
|
+
self.close_complete_token.cancel();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// Returns after close has been requested and all outstanding permits have been returned.
|
|
130
|
+
pub async fn close_complete(&self) {
|
|
131
|
+
self.close_complete_token.cancelled().await;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/// Acquire a permit if one is available and close was not requested.
|
|
135
|
+
pub fn try_acquire_owned(
|
|
136
|
+
self: &Arc<Self>,
|
|
137
|
+
) -> Result<TrackedOwnedMeteredSemPermit, TryAcquireError> {
|
|
138
|
+
if self.close_requested.load(Ordering::Acquire) {
|
|
139
|
+
return Err(TryAcquireError::Closed);
|
|
140
|
+
}
|
|
141
|
+
self.outstanding_permits.fetch_add(1, Ordering::Release);
|
|
142
|
+
let res = self.inner.try_acquire_owned();
|
|
143
|
+
if res.is_err() {
|
|
144
|
+
self.outstanding_permits.fetch_sub(1, Ordering::Release);
|
|
145
|
+
}
|
|
146
|
+
res.map(|permit| TrackedOwnedMeteredSemPermit {
|
|
147
|
+
inner: Some(permit),
|
|
148
|
+
on_drop: self.on_permit_dropped(),
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fn on_permit_dropped(self: &Arc<Self>) -> Box<dyn Fn() + Send + Sync> {
|
|
153
|
+
let sem = self.clone();
|
|
154
|
+
Box::new(move || {
|
|
155
|
+
sem.outstanding_permits.fetch_sub(1, Ordering::Release);
|
|
156
|
+
if sem.close_requested.load(Ordering::Acquire)
|
|
157
|
+
&& sem.outstanding_permits.load(Ordering::Acquire) == 0
|
|
158
|
+
{
|
|
159
|
+
sem.close_complete_token.cancel();
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/// Tracks an OwnedMeteredSemPermit and calls on_drop when dropped.
|
|
166
|
+
#[derive(DebugCustom)]
|
|
167
|
+
#[debug(fmt = "Tracked({inner:?})")]
|
|
168
|
+
pub(crate) struct TrackedOwnedMeteredSemPermit {
|
|
169
|
+
inner: Option<OwnedMeteredSemPermit>,
|
|
170
|
+
on_drop: Box<dyn Fn() + Send + Sync>,
|
|
171
|
+
}
|
|
172
|
+
impl From<TrackedOwnedMeteredSemPermit> for OwnedMeteredSemPermit {
|
|
173
|
+
fn from(mut value: TrackedOwnedMeteredSemPermit) -> Self {
|
|
174
|
+
value
|
|
175
|
+
.inner
|
|
176
|
+
.take()
|
|
177
|
+
.expect("Inner permit should be available")
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
impl Drop for TrackedOwnedMeteredSemPermit {
|
|
181
|
+
fn drop(&mut self) {
|
|
182
|
+
(self.on_drop)();
|
|
64
183
|
}
|
|
65
184
|
}
|
|
66
185
|
|
|
67
186
|
/// Wraps an [OwnedSemaphorePermit] to update metrics when it's dropped
|
|
68
187
|
pub(crate) struct OwnedMeteredSemPermit {
|
|
69
188
|
inner: OwnedSemaphorePermit,
|
|
70
|
-
|
|
189
|
+
/// See [MeteredSemaphore::unused_claimants]. If present when dropping, used to decrement the
|
|
190
|
+
/// count.
|
|
191
|
+
unused_claimants: Option<Arc<AtomicUsize>>,
|
|
192
|
+
record_fn: Box<dyn Fn(bool) + Send + Sync>,
|
|
71
193
|
}
|
|
72
194
|
impl Drop for OwnedMeteredSemPermit {
|
|
73
195
|
fn drop(&mut self) {
|
|
74
|
-
(self.
|
|
196
|
+
if let Some(uc) = self.unused_claimants.take() {
|
|
197
|
+
uc.fetch_sub(1, Ordering::Release);
|
|
198
|
+
}
|
|
199
|
+
(self.record_fn)(true)
|
|
75
200
|
}
|
|
76
201
|
}
|
|
77
202
|
impl Debug for OwnedMeteredSemPermit {
|
|
@@ -79,15 +204,30 @@ impl Debug for OwnedMeteredSemPermit {
|
|
|
79
204
|
self.inner.fmt(f)
|
|
80
205
|
}
|
|
81
206
|
}
|
|
82
|
-
#[cfg(feature = "save_wf_inputs")]
|
|
83
207
|
impl OwnedMeteredSemPermit {
|
|
208
|
+
/// Should be called once this permit is actually being "used" for the work it was meant to
|
|
209
|
+
/// permit.
|
|
210
|
+
pub(crate) fn into_used(mut self) -> UsedMeteredSemPermit {
|
|
211
|
+
if let Some(uc) = self.unused_claimants.take() {
|
|
212
|
+
uc.fetch_sub(1, Ordering::Release);
|
|
213
|
+
(self.record_fn)(false)
|
|
214
|
+
}
|
|
215
|
+
UsedMeteredSemPermit(self)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
#[derive(Debug)]
|
|
220
|
+
pub(crate) struct UsedMeteredSemPermit(OwnedMeteredSemPermit);
|
|
221
|
+
impl UsedMeteredSemPermit {
|
|
222
|
+
#[cfg(feature = "save_wf_inputs")]
|
|
84
223
|
pub(crate) fn fake_deserialized() -> Self {
|
|
85
224
|
let sem = Arc::new(Semaphore::new(1));
|
|
86
225
|
let inner = sem.try_acquire_owned().unwrap();
|
|
87
|
-
Self {
|
|
226
|
+
Self(OwnedMeteredSemPermit {
|
|
88
227
|
inner,
|
|
89
|
-
|
|
90
|
-
|
|
228
|
+
unused_claimants: None,
|
|
229
|
+
record_fn: Box::new(|_| {}),
|
|
230
|
+
})
|
|
91
231
|
}
|
|
92
232
|
}
|
|
93
233
|
|
|
@@ -174,4 +314,43 @@ mod tests {
|
|
|
174
314
|
allow_tx.send(()).unwrap();
|
|
175
315
|
assert_eq!(when_allowed.poll_next_unpin(&mut cx), Poll::Ready(None));
|
|
176
316
|
}
|
|
317
|
+
|
|
318
|
+
#[tokio::test]
|
|
319
|
+
async fn closable_semaphore_permit_drop_returns_permit() {
|
|
320
|
+
let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
|
|
321
|
+
let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
|
|
322
|
+
let perm = sem.try_acquire_owned().unwrap();
|
|
323
|
+
let permits = sem.outstanding_permits.load(Ordering::Acquire);
|
|
324
|
+
assert_eq!(permits, 1);
|
|
325
|
+
drop(perm);
|
|
326
|
+
let permits = sem.outstanding_permits.load(Ordering::Acquire);
|
|
327
|
+
assert_eq!(permits, 0);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
#[tokio::test]
|
|
331
|
+
async fn closable_semaphore_permit_drop_after_close_resolves_close_complete() {
|
|
332
|
+
let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
|
|
333
|
+
let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
|
|
334
|
+
let perm = sem.try_acquire_owned().unwrap();
|
|
335
|
+
sem.close();
|
|
336
|
+
drop(perm);
|
|
337
|
+
sem.close_complete().await;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
#[tokio::test]
|
|
341
|
+
async fn closable_semaphore_close_complete_ready_if_unused() {
|
|
342
|
+
let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
|
|
343
|
+
let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
|
|
344
|
+
sem.close();
|
|
345
|
+
sem.close_complete().await;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#[tokio::test]
|
|
349
|
+
async fn closable_semaphore_does_not_hand_out_permits_after_closed() {
|
|
350
|
+
let inner = MeteredSemaphore::new(2, MetricsContext::no_op(), |_, _| {});
|
|
351
|
+
let sem = ClosableMeteredSemaphore::new_arc(Arc::new(inner));
|
|
352
|
+
sem.close();
|
|
353
|
+
let perm = sem.try_acquire_owned().unwrap_err();
|
|
354
|
+
assert_matches!(perm, TryAcquireError::Closed);
|
|
355
|
+
}
|
|
177
356
|
}
|
|
@@ -4,7 +4,7 @@ use crate::{
|
|
|
4
4
|
build_fake_worker, build_mock_pollers, canned_histories, gen_assert_and_reply,
|
|
5
5
|
mock_manual_poller, mock_poller, mock_poller_from_resps, mock_worker, poll_and_reply,
|
|
6
6
|
single_hist_mock_sg, test_worker_cfg, MockPollCfg, MockWorkerInputs, MocksHolder,
|
|
7
|
-
ResponseType, WorkflowCachingPolicy, TEST_Q,
|
|
7
|
+
QueueResponse, ResponseType, WorkerExt, WorkflowCachingPolicy, TEST_Q,
|
|
8
8
|
},
|
|
9
9
|
worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
|
|
10
10
|
ActivityHeartbeat, Worker, WorkerConfigBuilder,
|
|
@@ -13,7 +13,8 @@ use futures::FutureExt;
|
|
|
13
13
|
use itertools::Itertools;
|
|
14
14
|
use std::{
|
|
15
15
|
cell::RefCell,
|
|
16
|
-
collections::{hash_map::Entry, HashMap, VecDeque},
|
|
16
|
+
collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
|
|
17
|
+
future,
|
|
17
18
|
rc::Rc,
|
|
18
19
|
sync::{
|
|
19
20
|
atomic::{AtomicUsize, Ordering},
|
|
@@ -23,14 +24,17 @@ use std::{
|
|
|
23
24
|
};
|
|
24
25
|
use temporal_client::WorkflowOptions;
|
|
25
26
|
use temporal_sdk::{ActivityOptions, WfContext};
|
|
26
|
-
use temporal_sdk_core_api::{
|
|
27
|
+
use temporal_sdk_core_api::{
|
|
28
|
+
errors::{CompleteActivityError, PollActivityError},
|
|
29
|
+
Worker as WorkerTrait,
|
|
30
|
+
};
|
|
27
31
|
use temporal_sdk_core_protos::{
|
|
28
32
|
coresdk::{
|
|
29
33
|
activity_result::{
|
|
30
34
|
activity_execution_result, activity_resolution, ActivityExecutionResult,
|
|
31
35
|
ActivityResolution, Success,
|
|
32
36
|
},
|
|
33
|
-
activity_task::{activity_task, ActivityTask},
|
|
37
|
+
activity_task::{activity_task, ActivityCancelReason, ActivityTask, Cancel},
|
|
34
38
|
workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
|
|
35
39
|
workflow_commands::{
|
|
36
40
|
ActivityCancellationType, CompleteWorkflowExecution, RequestCancelActivity,
|
|
@@ -42,6 +46,9 @@ use temporal_sdk_core_protos::{
|
|
|
42
46
|
temporal::api::{
|
|
43
47
|
command::v1::{command::Attributes, ScheduleActivityTaskCommandAttributes},
|
|
44
48
|
enums::v1::EventType,
|
|
49
|
+
history::v1::{
|
|
50
|
+
history_event::Attributes as EventAttributes, ActivityTaskScheduledEventAttributes,
|
|
51
|
+
},
|
|
45
52
|
workflowservice::v1::{
|
|
46
53
|
PollActivityTaskQueueResponse, RecordActivityTaskHeartbeatResponse,
|
|
47
54
|
RespondActivityTaskCanceledResponse, RespondActivityTaskCompletedResponse,
|
|
@@ -52,11 +59,10 @@ use temporal_sdk_core_protos::{
|
|
|
52
59
|
};
|
|
53
60
|
use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd, TestWorker};
|
|
54
61
|
use tokio::{sync::Barrier, time::sleep};
|
|
62
|
+
use tokio_util::sync::CancellationToken;
|
|
55
63
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
let _task_q = "q";
|
|
59
|
-
let mut tasks = VecDeque::from(vec![
|
|
64
|
+
fn three_tasks() -> VecDeque<PollActivityTaskQueueResponse> {
|
|
65
|
+
VecDeque::from(vec![
|
|
60
66
|
PollActivityTaskQueueResponse {
|
|
61
67
|
task_token: vec![1],
|
|
62
68
|
activity_id: "act1".to_string(),
|
|
@@ -72,7 +78,13 @@ async fn max_activities_respected() {
|
|
|
72
78
|
activity_id: "act3".to_string(),
|
|
73
79
|
..Default::default()
|
|
74
80
|
},
|
|
75
|
-
])
|
|
81
|
+
])
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#[tokio::test]
|
|
85
|
+
async fn max_activities_respected() {
|
|
86
|
+
let _task_q = "q";
|
|
87
|
+
let mut tasks = three_tasks();
|
|
76
88
|
let mut mock_client = mock_workflow_client();
|
|
77
89
|
mock_client
|
|
78
90
|
.expect_poll_activity_task()
|
|
@@ -121,7 +133,7 @@ async fn activity_not_found_returns_ok() {
|
|
|
121
133
|
})
|
|
122
134
|
.await
|
|
123
135
|
.unwrap();
|
|
124
|
-
core.
|
|
136
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
125
137
|
}
|
|
126
138
|
|
|
127
139
|
#[tokio::test]
|
|
@@ -217,12 +229,14 @@ async fn heartbeats_report_cancels_only_once() {
|
|
|
217
229
|
})
|
|
218
230
|
.await
|
|
219
231
|
.unwrap();
|
|
220
|
-
core.
|
|
232
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
221
233
|
}
|
|
222
234
|
|
|
223
235
|
#[tokio::test]
|
|
224
236
|
async fn activity_cancel_interrupts_poll() {
|
|
225
237
|
let mut mock_poller = mock_manual_poller();
|
|
238
|
+
let shutdown_token = CancellationToken::new();
|
|
239
|
+
let shutdown_token_clone = shutdown_token.clone();
|
|
226
240
|
let mut poll_resps = VecDeque::from(vec![
|
|
227
241
|
async {
|
|
228
242
|
Some(Ok(PollActivityTaskQueueResponse {
|
|
@@ -237,10 +251,15 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
237
251
|
Some(Ok(Default::default()))
|
|
238
252
|
}
|
|
239
253
|
.boxed(),
|
|
254
|
+
async move {
|
|
255
|
+
shutdown_token.cancelled().await;
|
|
256
|
+
None
|
|
257
|
+
}
|
|
258
|
+
.boxed(),
|
|
240
259
|
]);
|
|
241
260
|
mock_poller
|
|
242
261
|
.expect_poll()
|
|
243
|
-
.times(
|
|
262
|
+
.times(3)
|
|
244
263
|
.returning(move || poll_resps.pop_front().unwrap());
|
|
245
264
|
|
|
246
265
|
let mut mock_client = mock_manual_workflow_client();
|
|
@@ -289,11 +308,12 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
289
308
|
}
|
|
290
309
|
).await.unwrap();
|
|
291
310
|
last_finisher.store(2, Ordering::SeqCst);
|
|
311
|
+
shutdown_token_clone.cancel();
|
|
292
312
|
}
|
|
293
313
|
};
|
|
294
314
|
// So that we know we blocked
|
|
295
315
|
assert_eq!(last_finisher.load(Ordering::Acquire), 2);
|
|
296
|
-
core.
|
|
316
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
297
317
|
}
|
|
298
318
|
|
|
299
319
|
#[tokio::test]
|
|
@@ -342,13 +362,10 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
342
362
|
})
|
|
343
363
|
.collect::<Vec<_>>(),
|
|
344
364
|
);
|
|
345
|
-
// Because the mock is so fast, it's possible it can return before the cancel channel in
|
|
346
|
-
// the activity task poll selector. So, the final poll when there are no more tasks must
|
|
347
|
-
// take a while.
|
|
348
365
|
poll_resps.push_back(
|
|
349
366
|
async {
|
|
350
|
-
|
|
351
|
-
unreachable!(
|
|
367
|
+
future::pending::<()>().await;
|
|
368
|
+
unreachable!()
|
|
352
369
|
}
|
|
353
370
|
.boxed(),
|
|
354
371
|
);
|
|
@@ -431,7 +448,7 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
431
448
|
})
|
|
432
449
|
.await;
|
|
433
450
|
|
|
434
|
-
worker.
|
|
451
|
+
worker.drain_activity_poller_and_shutdown().await;
|
|
435
452
|
}
|
|
436
453
|
|
|
437
454
|
#[tokio::test]
|
|
@@ -483,7 +500,7 @@ async fn activity_timeout_no_double_resolve() {
|
|
|
483
500
|
)
|
|
484
501
|
.await;
|
|
485
502
|
|
|
486
|
-
core.
|
|
503
|
+
core.drain_pollers_and_shutdown().await;
|
|
487
504
|
}
|
|
488
505
|
|
|
489
506
|
#[tokio::test]
|
|
@@ -529,7 +546,7 @@ async fn can_heartbeat_acts_during_shutdown() {
|
|
|
529
546
|
})
|
|
530
547
|
.await
|
|
531
548
|
.unwrap();
|
|
532
|
-
|
|
549
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
533
550
|
}
|
|
534
551
|
|
|
535
552
|
/// Verifies that if a user has tried to record a heartbeat and then immediately after failed the
|
|
@@ -580,7 +597,7 @@ async fn complete_act_with_fail_flushes_heartbeat() {
|
|
|
580
597
|
})
|
|
581
598
|
.await
|
|
582
599
|
.unwrap();
|
|
583
|
-
core.
|
|
600
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
584
601
|
|
|
585
602
|
// Verify the last seen call to record a heartbeat had the last detail payload
|
|
586
603
|
let last_seen_payload = &last_seen_payload.take().unwrap().payloads[0];
|
|
@@ -686,7 +703,7 @@ async fn no_eager_activities_requested_when_worker_options_disable_remote_activi
|
|
|
686
703
|
.await
|
|
687
704
|
.unwrap();
|
|
688
705
|
|
|
689
|
-
core.
|
|
706
|
+
core.drain_pollers_and_shutdown().await;
|
|
690
707
|
|
|
691
708
|
assert_eq!(num_eager_requested.load(Ordering::Relaxed), 0);
|
|
692
709
|
}
|
|
@@ -753,11 +770,8 @@ async fn activity_tasks_from_completion_are_delivered() {
|
|
|
753
770
|
.times(3)
|
|
754
771
|
.returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
|
|
755
772
|
let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
|
|
756
|
-
let
|
|
757
|
-
|
|
758
|
-
.expect_poll()
|
|
759
|
-
.returning(|| futures::future::pending().boxed());
|
|
760
|
-
mock.set_act_poller(Box::new(mock_poller));
|
|
773
|
+
let act_tasks: Vec<QueueResponse<PollActivityTaskQueueResponse>> = vec![];
|
|
774
|
+
mock.set_act_poller(mock_poller_from_resps(act_tasks));
|
|
761
775
|
mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
|
|
762
776
|
let core = mock_worker(mock);
|
|
763
777
|
|
|
@@ -816,7 +830,7 @@ async fn activity_tasks_from_completion_are_delivered() {
|
|
|
816
830
|
.unwrap();
|
|
817
831
|
}
|
|
818
832
|
|
|
819
|
-
core.
|
|
833
|
+
core.drain_pollers_and_shutdown().await;
|
|
820
834
|
|
|
821
835
|
// Verify only a single eager activity was scheduled (the one on our worker's task queue)
|
|
822
836
|
assert_eq!(num_eager_requested.load(Ordering::Relaxed), 3);
|
|
@@ -828,11 +842,23 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
828
842
|
let mut t = TestHistoryBuilder::default();
|
|
829
843
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
830
844
|
t.add_full_wf_task();
|
|
831
|
-
let schedid = t.
|
|
845
|
+
let schedid = t.add(EventAttributes::ActivityTaskScheduledEventAttributes(
|
|
846
|
+
ActivityTaskScheduledEventAttributes {
|
|
847
|
+
activity_id: "1".to_string(),
|
|
848
|
+
activity_type: Some("act1".into()),
|
|
849
|
+
..Default::default()
|
|
850
|
+
},
|
|
851
|
+
));
|
|
832
852
|
let startid = t.add_activity_task_started(schedid);
|
|
833
853
|
t.add_activity_task_completed(schedid, startid, b"hi".into());
|
|
834
854
|
t.add_full_wf_task();
|
|
835
|
-
let schedid = t.
|
|
855
|
+
let schedid = t.add(EventAttributes::ActivityTaskScheduledEventAttributes(
|
|
856
|
+
ActivityTaskScheduledEventAttributes {
|
|
857
|
+
activity_id: "2".to_string(),
|
|
858
|
+
activity_type: Some("act2".into()),
|
|
859
|
+
..Default::default()
|
|
860
|
+
},
|
|
861
|
+
));
|
|
836
862
|
let startid = t.add_activity_task_started(schedid);
|
|
837
863
|
t.add_activity_task_completed(schedid, startid, b"hi".into());
|
|
838
864
|
t.add_full_wf_task();
|
|
@@ -902,19 +928,25 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
902
928
|
// First poll for activities twice, occupying both slots
|
|
903
929
|
let at1 = core.poll_activity_task().await.unwrap();
|
|
904
930
|
let at2 = core.poll_activity_task().await.unwrap();
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
931
|
+
let workflow_complete_token = CancellationToken::new();
|
|
932
|
+
let workflow_complete_token_clone = workflow_complete_token.clone();
|
|
933
|
+
|
|
934
|
+
worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| {
|
|
935
|
+
let complete_token = workflow_complete_token.clone();
|
|
936
|
+
async move {
|
|
937
|
+
ctx.activity(ActivityOptions {
|
|
938
|
+
activity_type: "act1".to_string(),
|
|
939
|
+
..Default::default()
|
|
940
|
+
})
|
|
941
|
+
.await;
|
|
942
|
+
ctx.activity(ActivityOptions {
|
|
943
|
+
activity_type: "act2".to_string(),
|
|
944
|
+
..Default::default()
|
|
945
|
+
})
|
|
946
|
+
.await;
|
|
947
|
+
complete_token.cancel();
|
|
948
|
+
Ok(().into())
|
|
949
|
+
}
|
|
918
950
|
});
|
|
919
951
|
|
|
920
952
|
worker
|
|
@@ -941,6 +973,13 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
941
973
|
.await
|
|
942
974
|
.unwrap();
|
|
943
975
|
barr.wait().await;
|
|
976
|
+
// Wait for workflow to complete in order for all eager activities to be requested before shutting down.
|
|
977
|
+
// After shutdown, no eager activities slots can be allocated.
|
|
978
|
+
workflow_complete_token_clone.cancelled().await;
|
|
979
|
+
core.initiate_shutdown();
|
|
980
|
+
// Even though this test requests eager activity tasks, none are returned in poll responses.
|
|
981
|
+
let err = core.poll_activity_task().await.unwrap_err();
|
|
982
|
+
assert_matches!(err, PollActivityError::ShutDown);
|
|
944
983
|
};
|
|
945
984
|
// This wf poll should *not* set the flag that it wants tasks back since both slots are
|
|
946
985
|
// occupied
|
|
@@ -974,7 +1013,7 @@ async fn retryable_net_error_exhaustion_is_nonfatal() {
|
|
|
974
1013
|
})
|
|
975
1014
|
.await
|
|
976
1015
|
.unwrap();
|
|
977
|
-
core.
|
|
1016
|
+
core.drain_activity_poller_and_shutdown().await;
|
|
978
1017
|
}
|
|
979
1018
|
|
|
980
1019
|
#[tokio::test]
|
|
@@ -1012,3 +1051,56 @@ async fn cant_complete_activity_with_unset_result_payload() {
|
|
|
1012
1051
|
Err(CompleteActivityError::MalformedActivityCompletion { .. })
|
|
1013
1052
|
)
|
|
1014
1053
|
}
|
|
1054
|
+
|
|
1055
|
+
#[tokio::test]
|
|
1056
|
+
async fn graceful_shutdown() {
|
|
1057
|
+
let _task_q = "q";
|
|
1058
|
+
let mut tasks = three_tasks();
|
|
1059
|
+
let mut mock_client = mock_workflow_client();
|
|
1060
|
+
mock_client
|
|
1061
|
+
.expect_poll_activity_task()
|
|
1062
|
+
.times(3)
|
|
1063
|
+
.returning(move |_, _| Ok(tasks.pop_front().unwrap()));
|
|
1064
|
+
// They shall all be reported as failed
|
|
1065
|
+
mock_client
|
|
1066
|
+
.expect_fail_activity_task()
|
|
1067
|
+
.times(3)
|
|
1068
|
+
.returning(|_, _| Ok(Default::default()));
|
|
1069
|
+
|
|
1070
|
+
let worker = Worker::new_test(
|
|
1071
|
+
test_worker_cfg()
|
|
1072
|
+
.graceful_shutdown_period(Duration::from_millis(500))
|
|
1073
|
+
.build()
|
|
1074
|
+
.unwrap(),
|
|
1075
|
+
mock_client,
|
|
1076
|
+
);
|
|
1077
|
+
|
|
1078
|
+
let _1 = worker.poll_activity_task().await.unwrap();
|
|
1079
|
+
let _2 = worker.poll_activity_task().await.unwrap();
|
|
1080
|
+
let _3 = worker.poll_activity_task().await.unwrap();
|
|
1081
|
+
|
|
1082
|
+
worker.initiate_shutdown();
|
|
1083
|
+
let expected_tts = HashSet::from([vec![1], vec![2], vec![3]]);
|
|
1084
|
+
let mut seen_tts = HashSet::new();
|
|
1085
|
+
for _ in 1..=3 {
|
|
1086
|
+
let cancel = worker.poll_activity_task().await.unwrap();
|
|
1087
|
+
assert_matches!(
|
|
1088
|
+
cancel.variant,
|
|
1089
|
+
Some(activity_task::Variant::Cancel(Cancel {
|
|
1090
|
+
reason: r
|
|
1091
|
+
})) if r == ActivityCancelReason::WorkerShutdown as i32
|
|
1092
|
+
);
|
|
1093
|
+
seen_tts.insert(cancel.task_token);
|
|
1094
|
+
}
|
|
1095
|
+
assert_eq!(expected_tts, seen_tts);
|
|
1096
|
+
for tt in seen_tts {
|
|
1097
|
+
worker
|
|
1098
|
+
.complete_activity_task(ActivityTaskCompletion {
|
|
1099
|
+
task_token: tt,
|
|
1100
|
+
result: Some(ActivityExecutionResult::cancel_from_details(None)),
|
|
1101
|
+
})
|
|
1102
|
+
.await
|
|
1103
|
+
.unwrap();
|
|
1104
|
+
}
|
|
1105
|
+
worker.drain_pollers_and_shutdown().await;
|
|
1106
|
+
}
|
|
@@ -88,11 +88,12 @@ async fn parent_cancels_child_wf(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
88
88
|
.await
|
|
89
89
|
.into_started()
|
|
90
90
|
.expect("Child should get started");
|
|
91
|
-
|
|
92
|
-
let
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
start_res.cancel(&ctx);
|
|
92
|
+
let stat = start_res
|
|
93
|
+
.result()
|
|
94
|
+
.await
|
|
95
|
+
.status
|
|
96
|
+
.expect("child wf result is ok");
|
|
96
97
|
assert_matches!(stat, child_workflow_result::Status::Cancelled(_));
|
|
97
98
|
Ok(().into())
|
|
98
99
|
}
|