@temporalio/core-bridge 0.19.2 → 0.20.2
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 +90 -157
- package/Cargo.toml +1 -0
- package/index.d.ts +11 -27
- 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/.buildkite/docker/Dockerfile +1 -1
- package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
- package/sdk-core/.cargo/config.toml +1 -0
- package/sdk-core/CODEOWNERS +1 -1
- package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +119 -86
- package/sdk-core/bridge-ffi/src/lib.rs +311 -315
- package/sdk-core/bridge-ffi/src/wrappers.rs +108 -113
- package/sdk-core/client/Cargo.toml +13 -9
- package/sdk-core/client/LICENSE.txt +23 -0
- package/sdk-core/client/src/lib.rs +286 -174
- package/sdk-core/client/src/metrics.rs +86 -12
- package/sdk-core/client/src/raw.rs +566 -0
- package/sdk-core/client/src/retry.rs +137 -99
- package/sdk-core/core/Cargo.toml +15 -10
- package/sdk-core/core/LICENSE.txt +23 -0
- package/sdk-core/core/benches/workflow_replay.rs +79 -0
- package/sdk-core/core/src/abstractions.rs +38 -0
- package/sdk-core/core/src/core_tests/activity_tasks.rs +108 -182
- package/sdk-core/core/src/core_tests/child_workflows.rs +16 -11
- package/sdk-core/core/src/core_tests/determinism.rs +24 -12
- package/sdk-core/core/src/core_tests/local_activities.rs +53 -27
- package/sdk-core/core/src/core_tests/mod.rs +30 -43
- package/sdk-core/core/src/core_tests/queries.rs +82 -81
- package/sdk-core/core/src/core_tests/workers.rs +111 -296
- package/sdk-core/core/src/core_tests/workflow_cancels.rs +4 -4
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +257 -242
- package/sdk-core/core/src/lib.rs +73 -318
- package/sdk-core/core/src/pollers/mod.rs +4 -6
- package/sdk-core/core/src/pollers/poll_buffer.rs +20 -14
- package/sdk-core/core/src/protosext/mod.rs +7 -10
- package/sdk-core/core/src/replay/mod.rs +11 -150
- package/sdk-core/core/src/telemetry/metrics.rs +35 -2
- package/sdk-core/core/src/telemetry/mod.rs +49 -16
- package/sdk-core/core/src/telemetry/prometheus_server.rs +14 -35
- package/sdk-core/core/src/test_help/mod.rs +104 -170
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +57 -34
- package/sdk-core/core/src/worker/activities/local_activities.rs +95 -23
- package/sdk-core/core/src/worker/activities.rs +23 -16
- package/sdk-core/core/src/worker/client/mocks.rs +86 -0
- package/sdk-core/core/src/worker/client.rs +209 -0
- package/sdk-core/core/src/worker/mod.rs +207 -108
- package/sdk-core/core/src/workflow/driven_workflow.rs +21 -6
- package/sdk-core/core/src/workflow/history_update.rs +107 -24
- package/sdk-core/core/src/workflow/machines/activity_state_machine.rs +2 -3
- package/sdk-core/core/src/workflow/machines/child_workflow_state_machine.rs +2 -3
- package/sdk-core/core/src/workflow/machines/mod.rs +20 -17
- package/sdk-core/core/src/workflow/machines/signal_external_state_machine.rs +56 -19
- package/sdk-core/core/src/workflow/machines/transition_coverage.rs +5 -0
- package/sdk-core/core/src/workflow/machines/upsert_search_attributes_state_machine.rs +230 -22
- package/sdk-core/core/src/workflow/machines/workflow_machines.rs +81 -115
- package/sdk-core/core/src/workflow/machines/workflow_task_state_machine.rs +4 -4
- package/sdk-core/core/src/workflow/mod.rs +13 -1
- package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +70 -11
- package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +65 -41
- package/sdk-core/core-api/Cargo.toml +9 -1
- package/sdk-core/core-api/LICENSE.txt +23 -0
- package/sdk-core/core-api/src/errors.rs +7 -38
- package/sdk-core/core-api/src/lib.rs +44 -52
- package/sdk-core/core-api/src/worker.rs +10 -2
- package/sdk-core/etc/deps.svg +127 -96
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -7
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +10 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +6 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +2 -1
- package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +12 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +25 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +19 -35
- package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -6
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +53 -11
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +14 -7
- package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +3 -5
- package/sdk-core/sdk/Cargo.toml +16 -2
- package/sdk-core/sdk/LICENSE.txt +23 -0
- package/sdk-core/sdk/src/interceptors.rs +11 -0
- package/sdk-core/sdk/src/lib.rs +139 -151
- package/sdk-core/sdk/src/workflow_context/options.rs +86 -1
- package/sdk-core/sdk/src/workflow_context.rs +36 -17
- package/sdk-core/sdk/src/workflow_future.rs +19 -25
- package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
- package/sdk-core/sdk-core-protos/build.rs +1 -0
- package/sdk-core/sdk-core-protos/src/history_info.rs +17 -4
- package/sdk-core/sdk-core-protos/src/lib.rs +251 -47
- package/sdk-core/test-utils/Cargo.toml +3 -1
- package/sdk-core/test-utils/src/canned_histories.rs +27 -0
- package/sdk-core/test-utils/src/histfetch.rs +3 -3
- package/sdk-core/test-utils/src/lib.rs +223 -68
- package/sdk-core/tests/integ_tests/client_tests.rs +27 -4
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +93 -14
- package/sdk-core/tests/integ_tests/polling_tests.rs +18 -12
- package/sdk-core/tests/integ_tests/queries_tests.rs +50 -53
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +117 -103
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +8 -1
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +10 -5
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +7 -1
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +32 -9
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +7 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +76 -15
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +19 -3
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +39 -42
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +84 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +30 -8
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +21 -6
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +26 -16
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +66 -0
- package/sdk-core/tests/integ_tests/workflow_tests.rs +78 -74
- package/sdk-core/tests/load_tests.rs +9 -6
- package/sdk-core/tests/main.rs +43 -10
- package/src/conversions.rs +7 -12
- package/src/lib.rs +322 -357
- package/sdk-core/client/src/mocks.rs +0 -167
- package/sdk-core/core/src/worker/dispatcher.rs +0 -171
- package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +0 -61
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
use crate::workflow::WFCommand;
|
|
1
|
+
use crate::workflow::{WFCommand, WorkflowStartedInfo};
|
|
2
2
|
use std::collections::VecDeque;
|
|
3
3
|
use temporal_sdk_core_protos::{
|
|
4
4
|
coresdk::workflow_activation::{
|
|
5
|
-
workflow_activation_job, CancelWorkflow, SignalWorkflow,
|
|
5
|
+
start_workflow_from_attribs, workflow_activation_job, CancelWorkflow, SignalWorkflow,
|
|
6
|
+
WorkflowActivationJob,
|
|
6
7
|
},
|
|
7
8
|
temporal::api::history::v1::WorkflowExecutionStartedEventAttributes,
|
|
9
|
+
utilities::TryIntoOrNone,
|
|
8
10
|
};
|
|
9
11
|
|
|
10
12
|
/// Abstracts away the concept of an actual workflow implementation, handling sending it new
|
|
11
13
|
/// jobs and fetching output from it.
|
|
12
14
|
pub struct DrivenWorkflow {
|
|
13
|
-
started_attrs: Option<
|
|
15
|
+
started_attrs: Option<WorkflowStartedInfo>,
|
|
14
16
|
fetcher: Box<dyn WorkflowFetcher>,
|
|
15
17
|
/// Outgoing activation jobs that need to be sent to the lang sdk
|
|
16
18
|
outgoing_wf_activation_jobs: VecDeque<workflow_activation_job::Variant>,
|
|
@@ -31,13 +33,26 @@ where
|
|
|
31
33
|
|
|
32
34
|
impl DrivenWorkflow {
|
|
33
35
|
/// Start the workflow
|
|
34
|
-
pub fn start(
|
|
36
|
+
pub fn start(
|
|
37
|
+
&mut self,
|
|
38
|
+
workflow_id: String,
|
|
39
|
+
randomness_seed: u64,
|
|
40
|
+
attribs: WorkflowExecutionStartedEventAttributes,
|
|
41
|
+
) {
|
|
35
42
|
debug!(run_id = %attribs.original_execution_run_id, "Driven WF start");
|
|
36
|
-
|
|
43
|
+
let started_info = WorkflowStartedInfo {
|
|
44
|
+
workflow_task_timeout: attribs.workflow_task_timeout.clone().try_into_or_none(),
|
|
45
|
+
workflow_execution_timeout: attribs
|
|
46
|
+
.workflow_execution_timeout
|
|
47
|
+
.clone()
|
|
48
|
+
.try_into_or_none(),
|
|
49
|
+
};
|
|
50
|
+
self.send_job(start_workflow_from_attribs(attribs, workflow_id, randomness_seed).into());
|
|
51
|
+
self.started_attrs = Some(started_info);
|
|
37
52
|
}
|
|
38
53
|
|
|
39
54
|
/// Return the attributes from the workflow execution started event if this workflow has started
|
|
40
|
-
pub fn
|
|
55
|
+
pub fn get_started_info(&self) -> Option<&WorkflowStartedInfo> {
|
|
41
56
|
self.started_attrs.as_ref()
|
|
42
57
|
}
|
|
43
58
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
replay::{HistoryInfo, TestHistoryBuilder},
|
|
3
|
-
|
|
3
|
+
worker::client::WorkerClientBag,
|
|
4
4
|
};
|
|
5
5
|
use futures::{future::BoxFuture, stream, stream::BoxStream, FutureExt, Stream, StreamExt};
|
|
6
6
|
use std::{
|
|
@@ -30,32 +30,84 @@ pub struct HistoryUpdate {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
pub struct HistoryPaginator {
|
|
33
|
-
|
|
33
|
+
// Potentially this could actually be a ref w/ lifetime here
|
|
34
|
+
client: Arc<WorkerClientBag>,
|
|
34
35
|
event_queue: VecDeque<HistoryEvent>,
|
|
35
36
|
wf_id: String,
|
|
36
37
|
run_id: String,
|
|
37
|
-
next_page_token:
|
|
38
|
+
next_page_token: NextPageToken,
|
|
38
39
|
open_history_request:
|
|
39
40
|
Option<BoxFuture<'static, Result<GetWorkflowExecutionHistoryResponse, tonic::Status>>>,
|
|
41
|
+
/// These are events that should be returned once pagination has finished. This only happens
|
|
42
|
+
/// during cache misses, where we got a partial task but need to fetch history from the start.
|
|
43
|
+
/// We use this to apply any
|
|
44
|
+
final_events: Vec<HistoryEvent>,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#[derive(Clone, Debug)]
|
|
48
|
+
pub enum NextPageToken {
|
|
49
|
+
/// There is no page token, we need to fetch history from the beginning
|
|
50
|
+
FetchFromStart,
|
|
51
|
+
/// There is a page token
|
|
52
|
+
Next(Vec<u8>),
|
|
53
|
+
/// There is no page token, we are done fetching history
|
|
54
|
+
Done,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// If we're converting from a page token from the server, if it's empty, then we're done.
|
|
58
|
+
impl From<Vec<u8>> for NextPageToken {
|
|
59
|
+
fn from(page_token: Vec<u8>) -> Self {
|
|
60
|
+
if page_token.is_empty() {
|
|
61
|
+
NextPageToken::Done
|
|
62
|
+
} else {
|
|
63
|
+
NextPageToken::Next(page_token)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
40
66
|
}
|
|
41
67
|
|
|
42
68
|
impl HistoryPaginator {
|
|
43
|
-
pub fn new(
|
|
69
|
+
pub(crate) fn new(
|
|
44
70
|
initial_history: History,
|
|
45
71
|
wf_id: String,
|
|
46
72
|
run_id: String,
|
|
47
|
-
next_page_token:
|
|
48
|
-
|
|
73
|
+
next_page_token: impl Into<NextPageToken>,
|
|
74
|
+
client: Arc<WorkerClientBag>,
|
|
49
75
|
) -> Self {
|
|
76
|
+
let next_page_token = next_page_token.into();
|
|
77
|
+
let (event_queue, final_events) =
|
|
78
|
+
if matches!(next_page_token, NextPageToken::FetchFromStart) {
|
|
79
|
+
(VecDeque::new(), initial_history.events)
|
|
80
|
+
} else {
|
|
81
|
+
(initial_history.events.into(), vec![])
|
|
82
|
+
};
|
|
50
83
|
Self {
|
|
51
|
-
|
|
52
|
-
event_queue
|
|
84
|
+
client,
|
|
85
|
+
event_queue,
|
|
53
86
|
wf_id,
|
|
54
87
|
run_id,
|
|
55
88
|
next_page_token,
|
|
56
89
|
open_history_request: None,
|
|
90
|
+
final_events,
|
|
57
91
|
}
|
|
58
92
|
}
|
|
93
|
+
|
|
94
|
+
fn extend_queue_with_new_page(&mut self, resp: GetWorkflowExecutionHistoryResponse) {
|
|
95
|
+
self.next_page_token = resp.next_page_token.into();
|
|
96
|
+
self.event_queue
|
|
97
|
+
.extend(resp.history.map(|h| h.events).unwrap_or_default());
|
|
98
|
+
if matches!(&self.next_page_token, NextPageToken::Done) {
|
|
99
|
+
// If finished, we need to extend the queue with the final events, skipping any
|
|
100
|
+
// which are already present.
|
|
101
|
+
if let Some(last_event_id) = self.event_queue.back().map(|e| e.event_id) {
|
|
102
|
+
let final_events = std::mem::take(&mut self.final_events);
|
|
103
|
+
self.event_queue.extend(
|
|
104
|
+
final_events
|
|
105
|
+
.into_iter()
|
|
106
|
+
.skip_while(|e2| e2.event_id <= last_event_id),
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
59
111
|
}
|
|
60
112
|
|
|
61
113
|
impl Stream for HistoryPaginator {
|
|
@@ -65,20 +117,19 @@ impl Stream for HistoryPaginator {
|
|
|
65
117
|
if let Some(e) = self.event_queue.pop_front() {
|
|
66
118
|
return Poll::Ready(Some(Ok(e)));
|
|
67
119
|
}
|
|
68
|
-
if self.next_page_token.is_empty() {
|
|
69
|
-
return Poll::Ready(None);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
120
|
let history_req = if let Some(req) = self.open_history_request.as_mut() {
|
|
73
121
|
req
|
|
74
122
|
} else {
|
|
123
|
+
let npt = match std::mem::replace(&mut self.next_page_token, NextPageToken::Done) {
|
|
124
|
+
// If there's no open request and the last page token we got was empty, we're done.
|
|
125
|
+
NextPageToken::Done => return Poll::Ready(None),
|
|
126
|
+
NextPageToken::FetchFromStart => vec![],
|
|
127
|
+
NextPageToken::Next(v) => v,
|
|
128
|
+
};
|
|
75
129
|
debug!(run_id=%self.run_id, "Fetching new history page");
|
|
76
|
-
|
|
77
|
-
// the server. Note that server can return page tokens that point to an empty page.
|
|
78
|
-
let gw = self.gateway.clone();
|
|
130
|
+
let gw = self.client.clone();
|
|
79
131
|
let wid = self.wf_id.clone();
|
|
80
132
|
let rid = self.run_id.clone();
|
|
81
|
-
let npt = self.next_page_token.clone();
|
|
82
133
|
let resp_fut =
|
|
83
134
|
async move { gw.get_workflow_execution_history(wid, Some(rid), npt).await };
|
|
84
135
|
self.open_history_request.insert(resp_fut.boxed())
|
|
@@ -90,9 +141,7 @@ impl Stream for HistoryPaginator {
|
|
|
90
141
|
match resp {
|
|
91
142
|
Err(neterr) => Poll::Ready(Some(Err(neterr))),
|
|
92
143
|
Ok(resp) => {
|
|
93
|
-
self.
|
|
94
|
-
self.event_queue
|
|
95
|
-
.extend(resp.history.map(|h| h.events).unwrap_or_default());
|
|
144
|
+
self.extend_queue_with_new_page(resp);
|
|
96
145
|
Poll::Ready(self.event_queue.pop_front().map(Ok))
|
|
97
146
|
}
|
|
98
147
|
}
|
|
@@ -255,8 +304,7 @@ impl TestHBExt for TestHistoryBuilder {
|
|
|
255
304
|
#[cfg(test)]
|
|
256
305
|
pub mod tests {
|
|
257
306
|
use super::*;
|
|
258
|
-
use crate::test_help::canned_histories;
|
|
259
|
-
use temporal_client::mocks::mock_gateway;
|
|
307
|
+
use crate::{test_help::canned_histories, worker::client::mocks::mock_workflow_client};
|
|
260
308
|
|
|
261
309
|
#[tokio::test]
|
|
262
310
|
async fn consumes_standard_wft_sequence() {
|
|
@@ -312,10 +360,10 @@ pub mod tests {
|
|
|
312
360
|
let long_hist = canned_histories::long_sequential_timers(wft_count);
|
|
313
361
|
let initial_hist = long_hist.get_history_info(10).unwrap();
|
|
314
362
|
let prev_started = initial_hist.previous_started_event_id();
|
|
315
|
-
let mut
|
|
363
|
+
let mut mock_client = mock_workflow_client();
|
|
316
364
|
|
|
317
365
|
let mut npt = 2;
|
|
318
|
-
|
|
366
|
+
mock_client
|
|
319
367
|
.expect_get_workflow_execution_history()
|
|
320
368
|
.returning(move |_, _, passed_npt| {
|
|
321
369
|
assert_eq!(passed_npt, vec![npt]);
|
|
@@ -335,7 +383,7 @@ pub mod tests {
|
|
|
335
383
|
"wfid".to_string(),
|
|
336
384
|
"runid".to_string(),
|
|
337
385
|
vec![2], // Start at page "2"
|
|
338
|
-
Arc::new(
|
|
386
|
+
Arc::new(mock_client.into()),
|
|
339
387
|
),
|
|
340
388
|
prev_started,
|
|
341
389
|
);
|
|
@@ -358,4 +406,39 @@ pub mod tests {
|
|
|
358
406
|
last_started_id += 5;
|
|
359
407
|
}
|
|
360
408
|
}
|
|
409
|
+
|
|
410
|
+
#[tokio::test]
|
|
411
|
+
async fn handles_cache_misses() {
|
|
412
|
+
let timer_hist = canned_histories::single_timer("t");
|
|
413
|
+
let partial_task = timer_hist.get_one_wft(2).unwrap();
|
|
414
|
+
let mut history_from_get: GetWorkflowExecutionHistoryResponse =
|
|
415
|
+
timer_hist.get_history_info(2).unwrap().into();
|
|
416
|
+
// Chop off the last event, which is WFT started, which server doesn't return in get
|
|
417
|
+
// history
|
|
418
|
+
history_from_get.history.as_mut().map(|h| h.events.pop());
|
|
419
|
+
let mut mock_client = mock_workflow_client();
|
|
420
|
+
mock_client
|
|
421
|
+
.expect_get_workflow_execution_history()
|
|
422
|
+
.returning(move |_, _, _| Ok(history_from_get.clone()));
|
|
423
|
+
|
|
424
|
+
let mut update = HistoryUpdate::new(
|
|
425
|
+
HistoryPaginator::new(
|
|
426
|
+
partial_task.into(),
|
|
427
|
+
"wfid".to_string(),
|
|
428
|
+
"runid".to_string(),
|
|
429
|
+
// A cache miss means we'll try to fetch from start
|
|
430
|
+
NextPageToken::FetchFromStart,
|
|
431
|
+
Arc::new(mock_client.into()),
|
|
432
|
+
),
|
|
433
|
+
1,
|
|
434
|
+
);
|
|
435
|
+
// We expect if we try to take the first task sequence that the first event is the first
|
|
436
|
+
// event in the sequence.
|
|
437
|
+
let seq = update.take_next_wft_sequence(0).await.unwrap();
|
|
438
|
+
assert_eq!(seq[0].event_id, 1);
|
|
439
|
+
let seq = update.take_next_wft_sequence(3).await.unwrap();
|
|
440
|
+
// Verify anything extra (which should only ever be WFT started) was re-appended to the
|
|
441
|
+
// end of the event iteration after fetching the old history.
|
|
442
|
+
assert_eq!(seq.last().unwrap().event_id, 8);
|
|
443
|
+
}
|
|
361
444
|
}
|
|
@@ -737,9 +737,8 @@ fn convert_payloads(
|
|
|
737
737
|
) -> Result<Option<Payload>, WFMachinesError> {
|
|
738
738
|
result.map(TryInto::try_into).transpose().map_err(|pe| {
|
|
739
739
|
WFMachinesError::Fatal(format!(
|
|
740
|
-
"Not exactly one payload in activity result ({}) for event: {}",
|
|
741
|
-
pe,
|
|
742
|
-
event_info.map(|e| e.event.clone()).unwrap_or_default()
|
|
740
|
+
"Not exactly one payload in activity result ({}) for event: {:?}",
|
|
741
|
+
pe, event_info
|
|
743
742
|
))
|
|
744
743
|
})
|
|
745
744
|
}
|
|
@@ -606,9 +606,8 @@ fn convert_payloads(
|
|
|
606
606
|
) -> Result<Option<Payload>, WFMachinesError> {
|
|
607
607
|
result.map(TryInto::try_into).transpose().map_err(|pe| {
|
|
608
608
|
WFMachinesError::Fatal(format!(
|
|
609
|
-
"Not exactly one payload in child workflow result ({}) for event: {}",
|
|
610
|
-
pe,
|
|
611
|
-
event_info.map(|e| e.event.clone()).unwrap_or_default()
|
|
609
|
+
"Not exactly one payload in child workflow result ({}) for event: {:?}",
|
|
610
|
+
pe, event_info
|
|
612
611
|
))
|
|
613
612
|
})
|
|
614
613
|
}
|
|
@@ -15,7 +15,6 @@ mod patch_state_machine;
|
|
|
15
15
|
mod side_effect_state_machine;
|
|
16
16
|
mod signal_external_state_machine;
|
|
17
17
|
mod timer_state_machine;
|
|
18
|
-
#[allow(unused)]
|
|
19
18
|
mod upsert_search_attributes_state_machine;
|
|
20
19
|
mod workflow_task_state_machine;
|
|
21
20
|
|
|
@@ -42,9 +41,12 @@ use std::{
|
|
|
42
41
|
fmt::{Debug, Display},
|
|
43
42
|
};
|
|
44
43
|
use temporal_sdk_core_protos::temporal::api::{
|
|
45
|
-
command::v1::Command as ProtoCommand,
|
|
44
|
+
command::v1::Command as ProtoCommand,
|
|
45
|
+
enums::v1::{CommandType, EventType},
|
|
46
|
+
history::v1::HistoryEvent,
|
|
46
47
|
};
|
|
47
48
|
use timer_state_machine::TimerMachine;
|
|
49
|
+
use upsert_search_attributes_state_machine::UpsertSearchAttributesMachine;
|
|
48
50
|
use workflow_machines::MachineResponse;
|
|
49
51
|
use workflow_task_state_machine::WorkflowTaskMachine;
|
|
50
52
|
|
|
@@ -65,6 +67,7 @@ enum MachineKind {
|
|
|
65
67
|
SignalExternalWorkflow,
|
|
66
68
|
CancelExternalWorkflow,
|
|
67
69
|
LocalActivity,
|
|
70
|
+
UpsertSearchAttributes,
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
#[enum_dispatch::enum_dispatch]
|
|
@@ -82,6 +85,7 @@ enum Machines {
|
|
|
82
85
|
SignalExternalMachine,
|
|
83
86
|
TimerMachine,
|
|
84
87
|
WorkflowTaskMachine,
|
|
88
|
+
UpsertSearchAttributesMachine,
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
/// Extends [rustfsm::StateMachine] with some functionality specific to the temporal SDK.
|
|
@@ -104,7 +108,7 @@ trait TemporalStateMachine: Send {
|
|
|
104
108
|
/// to update the overall state of the workflow. EX: To issue outgoing WF activations.
|
|
105
109
|
fn handle_event(
|
|
106
110
|
&mut self,
|
|
107
|
-
event:
|
|
111
|
+
event: HistoryEvent,
|
|
108
112
|
has_next_event: bool,
|
|
109
113
|
) -> Result<Vec<MachineResponse>, WFMachinesError>;
|
|
110
114
|
|
|
@@ -168,7 +172,7 @@ where
|
|
|
168
172
|
|
|
169
173
|
fn handle_event(
|
|
170
174
|
&mut self,
|
|
171
|
-
event:
|
|
175
|
+
event: HistoryEvent,
|
|
172
176
|
has_next_event: bool,
|
|
173
177
|
) -> Result<Vec<MachineResponse>, WFMachinesError> {
|
|
174
178
|
debug!(
|
|
@@ -177,22 +181,20 @@ where
|
|
|
177
181
|
state = %self.state(),
|
|
178
182
|
"handling event"
|
|
179
183
|
);
|
|
180
|
-
let
|
|
184
|
+
let event_info = EventInfo {
|
|
185
|
+
event_id: event.event_id,
|
|
186
|
+
event_type: event.event_type(),
|
|
187
|
+
has_next_event,
|
|
188
|
+
};
|
|
189
|
+
let converted_event: <Self as StateMachine>::Event = event.try_into()?;
|
|
181
190
|
|
|
182
191
|
match OnEventWrapper::on_event_mut(self, converted_event) {
|
|
183
|
-
Ok(c) => process_machine_commands(
|
|
184
|
-
self,
|
|
185
|
-
c,
|
|
186
|
-
Some(EventInfo {
|
|
187
|
-
event,
|
|
188
|
-
has_next_event,
|
|
189
|
-
}),
|
|
190
|
-
),
|
|
192
|
+
Ok(c) => process_machine_commands(self, c, Some(event_info)),
|
|
191
193
|
Err(MachineError::InvalidTransition) => Err(WFMachinesError::Fatal(format!(
|
|
192
|
-
"{} in state {} says the transition is invalid during event {}",
|
|
194
|
+
"{} in state {} says the transition is invalid during event {:?}",
|
|
193
195
|
self.name(),
|
|
194
196
|
self.state(),
|
|
195
|
-
|
|
197
|
+
event_info
|
|
196
198
|
))),
|
|
197
199
|
Err(MachineError::Underlying(e)) => Err(e.into()),
|
|
198
200
|
}
|
|
@@ -263,8 +265,9 @@ trait WFMachinesAdapter: StateMachine {
|
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
#[derive(Debug, Copy, Clone)]
|
|
266
|
-
struct EventInfo
|
|
267
|
-
|
|
268
|
+
struct EventInfo {
|
|
269
|
+
event_id: i64,
|
|
270
|
+
event_type: EventType,
|
|
268
271
|
has_next_event: bool,
|
|
269
272
|
}
|
|
270
273
|
|
|
@@ -6,8 +6,11 @@ use rustfsm::{fsm, MachineError, TransitionResult};
|
|
|
6
6
|
use std::convert::TryFrom;
|
|
7
7
|
use temporal_sdk_core_protos::{
|
|
8
8
|
coresdk::{
|
|
9
|
-
common::
|
|
9
|
+
common::NamespacedWorkflowExecution,
|
|
10
10
|
workflow_activation::ResolveSignalExternalWorkflow,
|
|
11
|
+
workflow_commands::{
|
|
12
|
+
signal_external_workflow_execution as sig_we, SignalExternalWorkflowExecution,
|
|
13
|
+
},
|
|
11
14
|
IntoPayloadsExt,
|
|
12
15
|
},
|
|
13
16
|
temporal::api::{
|
|
@@ -62,15 +65,29 @@ pub(super) enum SignalExternalCommand {
|
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
pub(super) fn new_external_signal(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
attrs: SignalExternalWorkflowExecution,
|
|
69
|
+
this_namespace: &str,
|
|
70
|
+
) -> Result<NewMachineWithCommand, WFMachinesError> {
|
|
71
|
+
let (workflow_execution, only_child) = match attrs.target {
|
|
72
|
+
None => {
|
|
73
|
+
return Err(WFMachinesError::Fatal(
|
|
74
|
+
"Signal external workflow command had empty target field".to_string(),
|
|
75
|
+
))
|
|
76
|
+
}
|
|
77
|
+
Some(sig_we::Target::ChildWorkflowId(wfid)) => (
|
|
78
|
+
NamespacedWorkflowExecution {
|
|
79
|
+
namespace: this_namespace.to_string(),
|
|
80
|
+
workflow_id: wfid,
|
|
81
|
+
run_id: "".to_string(),
|
|
82
|
+
},
|
|
83
|
+
true,
|
|
84
|
+
),
|
|
85
|
+
Some(sig_we::Target::WorkflowExecution(we)) => (we, false),
|
|
86
|
+
};
|
|
87
|
+
|
|
71
88
|
let mut s = SignalExternalMachine {
|
|
72
89
|
state: Created {}.into(),
|
|
73
|
-
shared_state: SharedState { seq },
|
|
90
|
+
shared_state: SharedState { seq: attrs.seq },
|
|
74
91
|
};
|
|
75
92
|
OnEventWrapper::on_event_mut(&mut s, SignalExternalMachineEvents::Schedule)
|
|
76
93
|
.expect("Scheduling signal external wf command doesn't fail");
|
|
@@ -81,9 +98,13 @@ pub(super) fn new_external_signal(
|
|
|
81
98
|
workflow_id: workflow_execution.workflow_id,
|
|
82
99
|
run_id: workflow_execution.run_id,
|
|
83
100
|
}),
|
|
84
|
-
header:
|
|
85
|
-
|
|
86
|
-
|
|
101
|
+
header: if attrs.headers.is_empty() {
|
|
102
|
+
None
|
|
103
|
+
} else {
|
|
104
|
+
Some(attrs.headers.into())
|
|
105
|
+
},
|
|
106
|
+
signal_name: attrs.signal_name,
|
|
107
|
+
input: attrs.args.into_payloads(),
|
|
87
108
|
// Is deprecated
|
|
88
109
|
control: "".to_string(),
|
|
89
110
|
child_workflow_only: only_child,
|
|
@@ -93,10 +114,10 @@ pub(super) fn new_external_signal(
|
|
|
93
114
|
command_type: CommandType::SignalExternalWorkflowExecution as i32,
|
|
94
115
|
attributes: Some(cmd_attrs),
|
|
95
116
|
};
|
|
96
|
-
NewMachineWithCommand {
|
|
117
|
+
Ok(NewMachineWithCommand {
|
|
97
118
|
command: cmd,
|
|
98
119
|
machine: s.into(),
|
|
99
|
-
}
|
|
120
|
+
})
|
|
100
121
|
}
|
|
101
122
|
|
|
102
123
|
#[derive(Default, Clone)]
|
|
@@ -287,7 +308,9 @@ mod tests {
|
|
|
287
308
|
use super::*;
|
|
288
309
|
use crate::{replay::TestHistoryBuilder, workflow::managed_wf::ManagedWFFunc};
|
|
289
310
|
use std::mem::discriminant;
|
|
290
|
-
use temporal_sdk::{
|
|
311
|
+
use temporal_sdk::{
|
|
312
|
+
CancellableFuture, SignalWorkflowOptions, WfContext, WorkflowFunction, WorkflowResult,
|
|
313
|
+
};
|
|
291
314
|
use temporal_sdk_core_protos::coresdk::workflow_activation::{
|
|
292
315
|
workflow_activation_job, WorkflowActivationJob,
|
|
293
316
|
};
|
|
@@ -295,9 +318,9 @@ mod tests {
|
|
|
295
318
|
const SIGNAME: &str = "signame";
|
|
296
319
|
|
|
297
320
|
async fn signal_sender(ctx: WfContext) -> WorkflowResult<()> {
|
|
298
|
-
let
|
|
299
|
-
|
|
300
|
-
|
|
321
|
+
let mut dat = SignalWorkflowOptions::new("fake_wid", "fake_rid", SIGNAME, [b"hi!"]);
|
|
322
|
+
dat.with_header("tupac", b"shakur");
|
|
323
|
+
let res = ctx.signal_workflow(dat).await;
|
|
301
324
|
if res.is_err() {
|
|
302
325
|
Err(anyhow::anyhow!("Signal fail!"))
|
|
303
326
|
} else {
|
|
@@ -325,12 +348,21 @@ mod tests {
|
|
|
325
348
|
let wff = WorkflowFunction::new(signal_sender);
|
|
326
349
|
let mut wfm = ManagedWFFunc::new(t, wff, vec![]);
|
|
327
350
|
wfm.get_next_activation().await.unwrap();
|
|
328
|
-
let cmds = wfm.get_server_commands().commands;
|
|
351
|
+
let mut cmds = wfm.get_server_commands().commands;
|
|
329
352
|
assert_eq!(cmds.len(), 1);
|
|
330
353
|
assert_eq!(
|
|
331
354
|
cmds[0].command_type(),
|
|
332
355
|
CommandType::SignalExternalWorkflowExecution
|
|
333
356
|
);
|
|
357
|
+
assert_matches!(
|
|
358
|
+
cmds.remove(0).attributes.unwrap(),
|
|
359
|
+
command::Attributes::SignalExternalWorkflowExecutionCommandAttributes(attrs) => {
|
|
360
|
+
assert_eq!(attrs.signal_name, SIGNAME);
|
|
361
|
+
assert_eq!(attrs.input.unwrap().payloads[0],
|
|
362
|
+
b"hi!".into());
|
|
363
|
+
assert_eq!(*attrs.header.unwrap().fields.get("tupac").unwrap(), b"shakur".into());
|
|
364
|
+
}
|
|
365
|
+
);
|
|
334
366
|
wfm.get_next_activation().await.unwrap();
|
|
335
367
|
let cmds = wfm.get_server_commands().commands;
|
|
336
368
|
assert_eq!(cmds.len(), 1);
|
|
@@ -354,7 +386,12 @@ mod tests {
|
|
|
354
386
|
t.add_workflow_execution_completed();
|
|
355
387
|
|
|
356
388
|
let wff = WorkflowFunction::new(|ctx: WfContext| async move {
|
|
357
|
-
let sig = ctx.signal_workflow(
|
|
389
|
+
let sig = ctx.signal_workflow(SignalWorkflowOptions::new(
|
|
390
|
+
"fake_wid",
|
|
391
|
+
"fake_rid",
|
|
392
|
+
SIGNAME,
|
|
393
|
+
[b"hi!"],
|
|
394
|
+
));
|
|
358
395
|
sig.cancel(&ctx);
|
|
359
396
|
let _res = sig.await;
|
|
360
397
|
Ok(().into())
|
|
@@ -77,6 +77,7 @@ mod machine_coverage_report {
|
|
|
77
77
|
fail_workflow_state_machine::FailWorkflowMachine,
|
|
78
78
|
local_activity_state_machine::LocalActivityMachine, patch_state_machine::PatchMachine,
|
|
79
79
|
signal_external_state_machine::SignalExternalMachine, timer_state_machine::TimerMachine,
|
|
80
|
+
upsert_search_attributes_state_machine::UpsertSearchAttributesMachine,
|
|
80
81
|
workflow_task_state_machine::WorkflowTaskMachine,
|
|
81
82
|
};
|
|
82
83
|
use rustfsm::StateMachine;
|
|
@@ -113,6 +114,7 @@ mod machine_coverage_report {
|
|
|
113
114
|
let mut signal_ext = SignalExternalMachine::visualizer().to_owned();
|
|
114
115
|
let mut cancel_ext = CancelExternalMachine::visualizer().to_owned();
|
|
115
116
|
let mut la_mach = LocalActivityMachine::visualizer().to_owned();
|
|
117
|
+
let mut upsert_search_attr = UpsertSearchAttributesMachine::visualizer().to_owned();
|
|
116
118
|
|
|
117
119
|
// This isn't at all efficient but doesn't need to be.
|
|
118
120
|
// Replace transitions in the vizzes with green color if they are covered.
|
|
@@ -133,6 +135,9 @@ mod machine_coverage_report {
|
|
|
133
135
|
m @ "SignalExternalMachine" => cover_transitions(m, &mut signal_ext, coverage),
|
|
134
136
|
m @ "CancelExternalMachine" => cover_transitions(m, &mut cancel_ext, coverage),
|
|
135
137
|
m @ "LocalActivityMachine" => cover_transitions(m, &mut la_mach, coverage),
|
|
138
|
+
m @ "UpsertSearchAttributesMachine" => {
|
|
139
|
+
cover_transitions(m, &mut upsert_search_attr, coverage)
|
|
140
|
+
}
|
|
136
141
|
m => panic!("Unknown machine {}", m),
|
|
137
142
|
}
|
|
138
143
|
}
|