@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,418 +0,0 @@
|
|
|
1
|
-
use crate::{
|
|
2
|
-
protosext::ValidPollWFTQResponse,
|
|
3
|
-
telemetry::metrics::{workflow_type, MetricsContext},
|
|
4
|
-
workflow::{
|
|
5
|
-
workflow_tasks::{OutstandingActivation, OutstandingTask, WorkflowMissingError},
|
|
6
|
-
HistoryUpdate, Result, WFMachinesError, WorkflowManager,
|
|
7
|
-
},
|
|
8
|
-
};
|
|
9
|
-
use futures::future::{BoxFuture, FutureExt};
|
|
10
|
-
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
|
11
|
-
use std::{
|
|
12
|
-
collections::HashMap,
|
|
13
|
-
fmt::Debug,
|
|
14
|
-
ops::{Deref, DerefMut},
|
|
15
|
-
sync::Arc,
|
|
16
|
-
};
|
|
17
|
-
use temporal_sdk_core_protos::coresdk::workflow_activation::WorkflowActivation;
|
|
18
|
-
|
|
19
|
-
/// Provides a thread-safe way to access workflow machines for specific workflow runs
|
|
20
|
-
pub(crate) struct WorkflowConcurrencyManager {
|
|
21
|
-
/// Maps run id -> data about and machines for that run
|
|
22
|
-
runs: RwLock<HashMap<String, ManagedRun>>,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
struct ManagedRun {
|
|
26
|
-
wfm: Arc<Mutex<WorkflowManager>>,
|
|
27
|
-
wft: Option<OutstandingTask>,
|
|
28
|
-
activation: Option<OutstandingActivation>,
|
|
29
|
-
metrics: MetricsContext,
|
|
30
|
-
/// If set, it indicates there is a buffered poll response from the server that applies to this
|
|
31
|
-
/// run. This can happen when lang takes too long to complete a task and the task times out, for
|
|
32
|
-
/// example. Upon next completion, the buffered response will be removed and can be made ready
|
|
33
|
-
/// to be returned from polling
|
|
34
|
-
buffered_resp: Option<ValidPollWFTQResponse>,
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
impl ManagedRun {
|
|
38
|
-
fn new(wfm: WorkflowManager, metrics: MetricsContext) -> Self {
|
|
39
|
-
Self {
|
|
40
|
-
wfm: Arc::new(Mutex::new(wfm)),
|
|
41
|
-
wft: None,
|
|
42
|
-
activation: None,
|
|
43
|
-
metrics,
|
|
44
|
-
buffered_resp: None,
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
impl WorkflowConcurrencyManager {
|
|
50
|
-
pub fn new() -> Self {
|
|
51
|
-
Self {
|
|
52
|
-
runs: Default::default(),
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/// Allows access to outstanding task for a run. Returns `None` if there is no knowledge of
|
|
57
|
-
/// the run at all, or if the run exists but there is no outstanding workflow task.
|
|
58
|
-
pub(crate) fn get_task(
|
|
59
|
-
&self,
|
|
60
|
-
run_id: &str,
|
|
61
|
-
) -> Option<impl Deref<Target = OutstandingTask> + '_> {
|
|
62
|
-
let readlock = self.runs.read();
|
|
63
|
-
if let Some(run) = readlock.get(run_id) {
|
|
64
|
-
if run.wft.is_some() {
|
|
65
|
-
Some(RwLockReadGuard::map(readlock, |hm| {
|
|
66
|
-
// Unwraps are safe because we hold the lock and just ensured run is in the map
|
|
67
|
-
hm.get(run_id).unwrap().wft.as_ref().unwrap()
|
|
68
|
-
}))
|
|
69
|
-
} else {
|
|
70
|
-
None
|
|
71
|
-
}
|
|
72
|
-
} else {
|
|
73
|
-
None
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/// Allows access to outstanding activation slot for a run. Returns `None` if there is no
|
|
78
|
-
/// knowledge of the run at all, or if the run exists but there is no outstanding activation.
|
|
79
|
-
pub(crate) fn get_activation(&self, run_id: &str) -> Option<OutstandingActivation> {
|
|
80
|
-
let readlock = self.runs.read();
|
|
81
|
-
if readlock.contains_key(run_id) {
|
|
82
|
-
readlock.get(run_id).unwrap().activation
|
|
83
|
-
} else {
|
|
84
|
-
None
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/// Allows mutable access to outstanding workflow task slot for a run
|
|
89
|
-
pub(crate) fn get_task_mut(
|
|
90
|
-
&self,
|
|
91
|
-
run_id: &str,
|
|
92
|
-
) -> Result<impl DerefMut<Target = Option<OutstandingTask>> + '_, WorkflowMissingError> {
|
|
93
|
-
let writelock = self.runs.write();
|
|
94
|
-
if writelock.contains_key(run_id) {
|
|
95
|
-
Ok(RwLockWriteGuard::map(writelock, |hm| {
|
|
96
|
-
// Unwrap is safe because we hold the lock and just ensured run is in the map
|
|
97
|
-
&mut hm.get_mut(run_id).unwrap().wft
|
|
98
|
-
}))
|
|
99
|
-
} else {
|
|
100
|
-
Err(WorkflowMissingError {
|
|
101
|
-
run_id: run_id.to_owned(),
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/// Fetch metrics context for a run
|
|
107
|
-
pub(crate) fn run_metrics(
|
|
108
|
-
&self,
|
|
109
|
-
run_id: &str,
|
|
110
|
-
) -> Option<impl Deref<Target = MetricsContext> + '_> {
|
|
111
|
-
let readlock = self.runs.read();
|
|
112
|
-
if readlock.get(run_id).is_some() {
|
|
113
|
-
Some(RwLockReadGuard::map(readlock, |hm| {
|
|
114
|
-
// Unwraps are safe because we hold the lock and just ensured run is in the map
|
|
115
|
-
&hm.get(run_id).unwrap().metrics
|
|
116
|
-
}))
|
|
117
|
-
} else {
|
|
118
|
-
None
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/// Stores some work if there is any outstanding WFT or activation for the run. If there was
|
|
123
|
-
/// not, returns the work back out inside the option.
|
|
124
|
-
pub fn buffer_resp_if_outstanding_work(
|
|
125
|
-
&self,
|
|
126
|
-
work: ValidPollWFTQResponse,
|
|
127
|
-
) -> Option<ValidPollWFTQResponse> {
|
|
128
|
-
let mut writelock = self.runs.write();
|
|
129
|
-
let run_id = &work.workflow_execution.run_id;
|
|
130
|
-
if let Some(mut run) = writelock.get_mut(run_id) {
|
|
131
|
-
if run.wft.is_some() || run.activation.is_some() {
|
|
132
|
-
debug!(run_id = %run_id, "Got new WFT for a run with outstanding work");
|
|
133
|
-
run.buffered_resp = Some(work);
|
|
134
|
-
None
|
|
135
|
-
} else {
|
|
136
|
-
Some(work)
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
Some(work)
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
pub fn insert_wft(
|
|
144
|
-
&self,
|
|
145
|
-
run_id: &str,
|
|
146
|
-
task: OutstandingTask,
|
|
147
|
-
) -> Result<(), WorkflowMissingError> {
|
|
148
|
-
let mut dereffer = self.get_task_mut(run_id)?;
|
|
149
|
-
*dereffer = Some(task);
|
|
150
|
-
Ok(())
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/// Indicate it's finished and remove any outstanding workflow task associated with the run
|
|
154
|
-
pub fn complete_wft(
|
|
155
|
-
&self,
|
|
156
|
-
run_id: &str,
|
|
157
|
-
sent_wft_complete_to_srv: bool,
|
|
158
|
-
) -> Option<OutstandingTask> {
|
|
159
|
-
// If the WFT completion wasn't sent to the server, but we did see the final event, we still
|
|
160
|
-
// want to clear the workflow task. This can really only happen in replay testing, where we
|
|
161
|
-
// will generate poll responses with complete history but no attached query, and such a WFT
|
|
162
|
-
// would never really exist. The server wouldn't send a workflow task with nothing to do,
|
|
163
|
-
// but they are very useful for testing complete replay.
|
|
164
|
-
let saw_final = self
|
|
165
|
-
.access_sync(run_id, |wfm| wfm.machines.have_seen_terminal_event)
|
|
166
|
-
.unwrap_or_default();
|
|
167
|
-
if !saw_final && !sent_wft_complete_to_srv {
|
|
168
|
-
return None;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let retme = if let Ok(ot) = self.get_task_mut(run_id).as_deref_mut() {
|
|
172
|
-
(*ot).take()
|
|
173
|
-
} else {
|
|
174
|
-
None
|
|
175
|
-
};
|
|
176
|
-
if let Some(ot) = &retme {
|
|
177
|
-
if let Some(m) = self.run_metrics(run_id) {
|
|
178
|
-
m.wf_task_latency(ot.start_time.elapsed());
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
retme
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
pub fn insert_activation(
|
|
185
|
-
&self,
|
|
186
|
-
run_id: &str,
|
|
187
|
-
activation: OutstandingActivation,
|
|
188
|
-
) -> Result<Option<OutstandingActivation>, WorkflowMissingError> {
|
|
189
|
-
let mut writelock = self.runs.write();
|
|
190
|
-
let machine_ref = writelock.get_mut(run_id);
|
|
191
|
-
if let Some(run) = machine_ref {
|
|
192
|
-
Ok(run.activation.replace(activation))
|
|
193
|
-
} else {
|
|
194
|
-
Err(WorkflowMissingError {
|
|
195
|
-
run_id: run_id.to_owned(),
|
|
196
|
-
})
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
pub fn delete_activation(&self, run_id: &str) -> Option<OutstandingActivation> {
|
|
201
|
-
let mut writelock = self.runs.write();
|
|
202
|
-
let machine_ref = writelock.get_mut(run_id);
|
|
203
|
-
machine_ref.and_then(|run| run.activation.take())
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
pub fn exists(&self, run_id: &str) -> bool {
|
|
207
|
-
self.runs.read().get(run_id).is_some()
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/// Create or update some workflow's machines. Borrowed arguments are cloned in the case of a
|
|
211
|
-
/// new workflow instance.
|
|
212
|
-
pub async fn create_or_update(
|
|
213
|
-
&self,
|
|
214
|
-
run_id: &str,
|
|
215
|
-
history: HistoryUpdate,
|
|
216
|
-
workflow_id: &str,
|
|
217
|
-
namespace: &str,
|
|
218
|
-
wf_type: &str,
|
|
219
|
-
parent_metrics: &MetricsContext,
|
|
220
|
-
) -> Result<WorkflowActivation> {
|
|
221
|
-
let span = debug_span!("create_or_update machines", %run_id);
|
|
222
|
-
|
|
223
|
-
if self.runs.read().contains_key(run_id) {
|
|
224
|
-
let activation = self
|
|
225
|
-
.access(run_id, move |wfm: &mut WorkflowManager| {
|
|
226
|
-
async move {
|
|
227
|
-
let _enter = span.enter();
|
|
228
|
-
wfm.machines.metrics.sticky_cache_hit();
|
|
229
|
-
wfm.feed_history_from_server(history).await
|
|
230
|
-
}
|
|
231
|
-
.boxed()
|
|
232
|
-
})
|
|
233
|
-
.await?;
|
|
234
|
-
Ok(activation)
|
|
235
|
-
} else {
|
|
236
|
-
// Create a new workflow machines instance for this workflow, initialize it, and
|
|
237
|
-
// track it.
|
|
238
|
-
let metrics = parent_metrics.with_new_attrs([workflow_type(wf_type.to_string())]);
|
|
239
|
-
let mut wfm = WorkflowManager::new(
|
|
240
|
-
history,
|
|
241
|
-
namespace.to_owned(),
|
|
242
|
-
workflow_id.to_owned(),
|
|
243
|
-
wf_type.to_owned(),
|
|
244
|
-
run_id.to_owned(),
|
|
245
|
-
metrics.clone(),
|
|
246
|
-
);
|
|
247
|
-
match wfm.get_next_activation().await {
|
|
248
|
-
Ok(activation) => {
|
|
249
|
-
if activation.jobs.is_empty() {
|
|
250
|
-
Err(WFMachinesError::Fatal(
|
|
251
|
-
"Machines created with no jobs".to_string(),
|
|
252
|
-
))
|
|
253
|
-
} else {
|
|
254
|
-
self.runs
|
|
255
|
-
.write()
|
|
256
|
-
.insert(run_id.to_string(), ManagedRun::new(wfm, metrics));
|
|
257
|
-
Ok(activation)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
Err(e) => Err(e),
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
pub async fn access<F, Fout>(&self, run_id: &str, mutator: F) -> Result<Fout>
|
|
266
|
-
where
|
|
267
|
-
F: for<'a> FnOnce(&'a mut WorkflowManager) -> BoxFuture<Result<Fout>>,
|
|
268
|
-
Fout: Send + Debug,
|
|
269
|
-
{
|
|
270
|
-
// TODO: Slightly less than ideal. We must avoid holding the read lock on the overall
|
|
271
|
-
// machine map while async-ly mutating the inner machine. So, we clone the inner ArcMutex.
|
|
272
|
-
// We should restructure things to avoid the top-level lock on the map.
|
|
273
|
-
|
|
274
|
-
let wfm = {
|
|
275
|
-
let readlock = self.runs.read();
|
|
276
|
-
let m = readlock
|
|
277
|
-
.get(run_id)
|
|
278
|
-
.ok_or_else(|| WFMachinesError::Fatal("Missing workflow machines".to_string()))?;
|
|
279
|
-
m.wfm.clone()
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
let res = mutator(&mut wfm.lock()).await;
|
|
283
|
-
res
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
pub fn access_sync<F, Fout>(
|
|
287
|
-
&self,
|
|
288
|
-
run_id: &str,
|
|
289
|
-
mutator: F,
|
|
290
|
-
) -> Result<Fout, WorkflowMissingError>
|
|
291
|
-
where
|
|
292
|
-
F: for<'a> FnOnce(&'a mut WorkflowManager) -> Fout,
|
|
293
|
-
Fout: Send + Debug,
|
|
294
|
-
{
|
|
295
|
-
let readlock = self.runs.read();
|
|
296
|
-
let m = readlock.get(run_id).ok_or_else(|| WorkflowMissingError {
|
|
297
|
-
run_id: run_id.to_string(),
|
|
298
|
-
})?;
|
|
299
|
-
let mut wfm_mutex = m.wfm.lock();
|
|
300
|
-
Ok(mutator(&mut wfm_mutex))
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/// Remove the workflow with the provided run id from management
|
|
304
|
-
pub fn evict(&self, run_id: &str) -> Option<ValidPollWFTQResponse> {
|
|
305
|
-
let val = self.runs.write().remove(run_id);
|
|
306
|
-
val.and_then(|v| v.buffered_resp)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/// Clear and return any buffered polling response for this run ID
|
|
310
|
-
pub fn take_buffered_poll(&self, run_id: &str) -> Option<ValidPollWFTQResponse> {
|
|
311
|
-
let mut writelock = self.runs.write();
|
|
312
|
-
let val = writelock.get_mut(run_id);
|
|
313
|
-
val.and_then(|v| v.buffered_resp.take())
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/// Sounds the total number of outstanding workflow tasks
|
|
317
|
-
pub fn outstanding_wft(&self) -> usize {
|
|
318
|
-
self.runs
|
|
319
|
-
.read()
|
|
320
|
-
.iter()
|
|
321
|
-
.filter(|(_, run)| run.wft.is_some())
|
|
322
|
-
.count()
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/// Returns number of currently cached workflows
|
|
326
|
-
pub fn cached_workflows(&self) -> usize {
|
|
327
|
-
self.runs.read().len()
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/// Returns true if any outstanding activation contains an eviction
|
|
331
|
-
pub fn are_outstanding_evictions(&self) -> bool {
|
|
332
|
-
self.runs
|
|
333
|
-
.read()
|
|
334
|
-
.values()
|
|
335
|
-
.any(|mr| mr.activation.map(|a| a.has_eviction()).unwrap_or_default())
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
#[cfg(test)]
|
|
340
|
-
mod tests {
|
|
341
|
-
use super::*;
|
|
342
|
-
use crate::test_help::canned_histories;
|
|
343
|
-
use tokio::sync::Barrier;
|
|
344
|
-
|
|
345
|
-
// We test mostly error paths here since the happy paths are well covered by the tests of the
|
|
346
|
-
// core sdk itself, and setting up the fake data is onerous here. If we make the concurrency
|
|
347
|
-
// manager generic, testing the happy path is simpler.
|
|
348
|
-
|
|
349
|
-
#[tokio::test]
|
|
350
|
-
async fn returns_errors_on_creation() {
|
|
351
|
-
let mgr = WorkflowConcurrencyManager::new();
|
|
352
|
-
let res = mgr
|
|
353
|
-
.create_or_update(
|
|
354
|
-
"some_run_id",
|
|
355
|
-
HistoryUpdate::new_from_events(vec![], 0),
|
|
356
|
-
"fake_wf_id",
|
|
357
|
-
"fake_namespace",
|
|
358
|
-
"fake_wf_type",
|
|
359
|
-
&Default::default(),
|
|
360
|
-
)
|
|
361
|
-
.await;
|
|
362
|
-
// Should whine that the machines have nothing to do (history empty)
|
|
363
|
-
assert_matches!(res.unwrap_err(), WFMachinesError::Fatal { .. });
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/// This test makes sure that if we're stuck on an await within the machine mutator we don't
|
|
367
|
-
/// cause a deadlock if a write happens during that. This test will hang without proper
|
|
368
|
-
/// implementation.
|
|
369
|
-
#[tokio::test]
|
|
370
|
-
async fn aba_deadlock_prevented() {
|
|
371
|
-
let run_id = "some_run_id";
|
|
372
|
-
let timer_hist = canned_histories::single_timer("t");
|
|
373
|
-
let access_barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
|
|
374
|
-
let wft = timer_hist.get_history_info(1).unwrap();
|
|
375
|
-
|
|
376
|
-
let mgr = WorkflowConcurrencyManager::new();
|
|
377
|
-
mgr.create_or_update(
|
|
378
|
-
run_id,
|
|
379
|
-
wft.clone().into(),
|
|
380
|
-
"fake_wf_id",
|
|
381
|
-
"fake_namespace",
|
|
382
|
-
"fake_wf_type",
|
|
383
|
-
&Default::default(),
|
|
384
|
-
)
|
|
385
|
-
.await
|
|
386
|
-
.unwrap();
|
|
387
|
-
// Perform access which blocks
|
|
388
|
-
let access_fut = mgr.access(run_id, |_wfm| {
|
|
389
|
-
async {
|
|
390
|
-
// Wait to make sure access has started
|
|
391
|
-
access_barr.wait().await;
|
|
392
|
-
// Wait to make sure write has finished
|
|
393
|
-
access_barr.wait().await;
|
|
394
|
-
Ok(())
|
|
395
|
-
}
|
|
396
|
-
.boxed()
|
|
397
|
-
});
|
|
398
|
-
let write_fut = async {
|
|
399
|
-
// Wait to make sure access has started
|
|
400
|
-
access_barr.wait().await;
|
|
401
|
-
// Now try writing
|
|
402
|
-
mgr.create_or_update(
|
|
403
|
-
"different_run_id",
|
|
404
|
-
wft.clone().into(),
|
|
405
|
-
"fake_wf_id",
|
|
406
|
-
"fake_namespace",
|
|
407
|
-
"fake_wf_type",
|
|
408
|
-
&Default::default(),
|
|
409
|
-
)
|
|
410
|
-
.await
|
|
411
|
-
.unwrap();
|
|
412
|
-
// Indicate write has finished
|
|
413
|
-
access_barr.wait().await;
|
|
414
|
-
};
|
|
415
|
-
let (r1, _) = tokio::join!(access_fut, write_fut);
|
|
416
|
-
r1.unwrap();
|
|
417
|
-
}
|
|
418
|
-
}
|