@temporalio/core-bridge 0.21.1 → 0.22.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 +1 -0
- package/index.d.ts +6 -2
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/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 +5 -4
- package/sdk-core/client/Cargo.toml +1 -0
- package/sdk-core/client/src/lib.rs +52 -9
- package/sdk-core/client/src/raw.rs +9 -1
- package/sdk-core/client/src/retry.rs +12 -1
- package/sdk-core/client/src/workflow_handle/mod.rs +183 -0
- package/sdk-core/core/src/core_tests/child_workflows.rs +7 -9
- package/sdk-core/core/src/core_tests/determinism.rs +8 -19
- package/sdk-core/core/src/core_tests/local_activities.rs +22 -32
- package/sdk-core/core/src/core_tests/queries.rs +127 -3
- package/sdk-core/core/src/core_tests/workers.rs +4 -34
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +197 -9
- package/sdk-core/core/src/pending_activations.rs +11 -0
- package/sdk-core/core/src/telemetry/mod.rs +1 -1
- package/sdk-core/core/src/test_help/mod.rs +57 -7
- package/sdk-core/core/src/worker/mod.rs +60 -12
- package/sdk-core/core/src/workflow/machines/mod.rs +1 -1
- package/sdk-core/core/src/workflow/machines/timer_state_machine.rs +2 -2
- package/sdk-core/core/src/workflow/machines/workflow_machines.rs +5 -1
- package/sdk-core/core/src/workflow/workflow_tasks/cache_manager.rs +47 -2
- package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +16 -2
- package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +185 -96
- package/sdk-core/core-api/src/worker.rs +9 -0
- package/sdk-core/sdk/Cargo.toml +1 -0
- package/sdk-core/sdk/src/activity_context.rs +223 -0
- package/sdk-core/sdk/src/interceptors.rs +8 -2
- package/sdk-core/sdk/src/lib.rs +167 -122
- package/sdk-core/test-utils/Cargo.toml +1 -0
- package/sdk-core/test-utils/src/lib.rs +78 -37
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +11 -4
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +0 -1
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +0 -3
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +33 -17
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +10 -1
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +0 -1
- package/sdk-core/tests/integ_tests/workflow_tests.rs +71 -3
- package/sdk-core/tests/load_tests.rs +80 -6
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
package/Cargo.lock
CHANGED
package/index.d.ts
CHANGED
|
@@ -66,9 +66,13 @@ export interface TelemetryOptions {
|
|
|
66
66
|
* Which determines what tracing data is collected in the Core SDK
|
|
67
67
|
*/
|
|
68
68
|
tracingFilter?: string;
|
|
69
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* What level, if any, logs should be forwarded from core at
|
|
71
|
+
*
|
|
72
|
+
* @default OFF
|
|
73
|
+
*/
|
|
70
74
|
// These strings should match the log::LevelFilter enum in rust
|
|
71
|
-
logForwardingLevel
|
|
75
|
+
logForwardingLevel?: 'OFF' | LogLevel;
|
|
72
76
|
/** If set, metrics will be exposed on an http server in this process for direct scraping by
|
|
73
77
|
* prometheus. If used in conjunction with the OTel collector, metrics will *not* be exported
|
|
74
78
|
* to the collector, but traces will be.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@temporalio/core-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Temporal.io SDK Core<>Node bridge",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@opentelemetry/api": "^1.0.3",
|
|
23
|
-
"@temporalio/internal-non-workflow-common": "^0.
|
|
23
|
+
"@temporalio/internal-non-workflow-common": "^0.22.0",
|
|
24
24
|
"arg": "^5.0.1",
|
|
25
25
|
"cargo-cp-artifact": "^0.1.4",
|
|
26
26
|
"which": "^2.0.2"
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "3aa1f14982bd170d21b728cbf016dc4f1b595a76"
|
|
47
47
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -9,7 +9,7 @@ services:
|
|
|
9
9
|
# - '9042:9042'
|
|
10
10
|
|
|
11
11
|
temporal:
|
|
12
|
-
image: temporalio/auto-setup:1.
|
|
12
|
+
image: temporalio/auto-setup:1.16.0
|
|
13
13
|
ports:
|
|
14
14
|
- "7233:7233"
|
|
15
15
|
- "7234:7234"
|
|
@@ -26,12 +26,13 @@ services:
|
|
|
26
26
|
- cassandra
|
|
27
27
|
|
|
28
28
|
temporal-web:
|
|
29
|
-
image: temporalio/
|
|
29
|
+
image: temporalio/ui:0.10.2
|
|
30
30
|
logging:
|
|
31
31
|
driver: none
|
|
32
32
|
ports:
|
|
33
|
-
- "
|
|
33
|
+
- "8080:8080"
|
|
34
34
|
environment:
|
|
35
|
-
-
|
|
35
|
+
- TEMPORAL_ADDRESS=temporal:7233
|
|
36
|
+
- TEMPORAL_CORS_ORIGINS=http://localhost:3000
|
|
36
37
|
depends_on:
|
|
37
38
|
- temporal
|
|
@@ -10,13 +10,17 @@ extern crate tracing;
|
|
|
10
10
|
mod metrics;
|
|
11
11
|
mod raw;
|
|
12
12
|
mod retry;
|
|
13
|
+
mod workflow_handle;
|
|
13
14
|
|
|
14
15
|
pub use crate::retry::{CallType, RetryClient};
|
|
15
16
|
pub use raw::WorkflowService;
|
|
17
|
+
pub use workflow_handle::{WorkflowExecutionInfo, WorkflowExecutionResult};
|
|
16
18
|
|
|
17
19
|
use crate::{
|
|
18
20
|
metrics::{GrpcMetricSvc, MetricsContext},
|
|
19
21
|
raw::{sealed::RawClientLike, AttachMetricLabels},
|
|
22
|
+
sealed::{RawClientLikeUser, WfHandleClient},
|
|
23
|
+
workflow_handle::UntypedWorkflowHandle,
|
|
20
24
|
};
|
|
21
25
|
use backoff::{ExponentialBackoff, SystemClock};
|
|
22
26
|
use http::uri::InvalidUri;
|
|
@@ -37,7 +41,7 @@ use temporal_sdk_core_protos::{
|
|
|
37
41
|
enums::v1::{TaskQueueKind, WorkflowTaskFailedCause},
|
|
38
42
|
failure::v1::Failure,
|
|
39
43
|
query::v1::{WorkflowQuery, WorkflowQueryResult},
|
|
40
|
-
taskqueue::v1::{StickyExecutionAttributes, TaskQueue},
|
|
44
|
+
taskqueue::v1::{StickyExecutionAttributes, TaskQueue, TaskQueueMetadata},
|
|
41
45
|
workflowservice::v1::{workflow_service_client::WorkflowServiceClient, *},
|
|
42
46
|
},
|
|
43
47
|
TaskToken,
|
|
@@ -53,8 +57,6 @@ use tower::ServiceBuilder;
|
|
|
53
57
|
use url::Url;
|
|
54
58
|
use uuid::Uuid;
|
|
55
59
|
|
|
56
|
-
use temporal_sdk_core_protos::temporal::api::taskqueue::v1::TaskQueueMetadata;
|
|
57
|
-
|
|
58
60
|
static LONG_POLL_METHOD_NAMES: [&str; 2] = ["PollWorkflowTaskQueue", "PollActivityTaskQueue"];
|
|
59
61
|
/// The server times out polls after 60 seconds. Set our timeout to be slightly beyond that.
|
|
60
62
|
const LONG_POLL_TIMEOUT: Duration = Duration::from_secs(70);
|
|
@@ -431,7 +433,7 @@ pub type WorkflowServiceClientWithMetrics = WorkflowServiceClient<InterceptedMet
|
|
|
431
433
|
type InterceptedMetricsSvc = InterceptedService<GrpcMetricSvc, ServiceCallInterceptor>;
|
|
432
434
|
|
|
433
435
|
/// Contains an instance of a namespace-bound client for interacting with the Temporal server
|
|
434
|
-
#[derive(Debug)]
|
|
436
|
+
#[derive(Debug, Clone)]
|
|
435
437
|
pub struct Client {
|
|
436
438
|
/// Client for interacting with workflow service
|
|
437
439
|
inner: ConfiguredClient<WorkflowServiceClientWithMetrics>,
|
|
@@ -475,11 +477,6 @@ impl Client {
|
|
|
475
477
|
pub fn options(&self) -> &ClientOptions {
|
|
476
478
|
&self.inner.options
|
|
477
479
|
}
|
|
478
|
-
|
|
479
|
-
/// Used to access the client as a [WorkflowService] implementor rather than the raw struct
|
|
480
|
-
fn wf_svc(&self) -> impl RawClientLike<SvcType = InterceptedMetricsSvc> {
|
|
481
|
-
self.raw_client().clone()
|
|
482
|
-
}
|
|
483
480
|
}
|
|
484
481
|
|
|
485
482
|
/// This trait provides higher-level friendlier interaction with the server.
|
|
@@ -1036,3 +1033,49 @@ impl WorkflowClientTrait for Client {
|
|
|
1036
1033
|
&self.namespace
|
|
1037
1034
|
}
|
|
1038
1035
|
}
|
|
1036
|
+
|
|
1037
|
+
mod sealed {
|
|
1038
|
+
use crate::{InterceptedMetricsSvc, RawClientLike, WorkflowClientTrait};
|
|
1039
|
+
|
|
1040
|
+
pub trait RawClientLikeUser {
|
|
1041
|
+
type RawClientT: RawClientLike<SvcType = InterceptedMetricsSvc>;
|
|
1042
|
+
/// Used to access the client as a [WorkflowService] implementor rather than the raw struct
|
|
1043
|
+
fn wf_svc(&self) -> Self::RawClientT;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
pub trait WfHandleClient: WorkflowClientTrait + RawClientLikeUser {}
|
|
1047
|
+
impl<T> WfHandleClient for T where T: WorkflowClientTrait + RawClientLikeUser {}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
impl RawClientLikeUser for Client {
|
|
1051
|
+
type RawClientT = WorkflowServiceClientWithMetrics;
|
|
1052
|
+
|
|
1053
|
+
fn wf_svc(&self) -> Self::RawClientT {
|
|
1054
|
+
self.raw_client().clone()
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/// Additional methods for workflow clients
|
|
1059
|
+
pub trait WfClientExt: WfHandleClient + Sized {
|
|
1060
|
+
/// Create an untyped handle for a workflow execution, which can be used to do things like
|
|
1061
|
+
/// wait for that workflow's result. `run_id` may be left blank to target the latest run.
|
|
1062
|
+
fn get_untyped_workflow_handle(
|
|
1063
|
+
&self,
|
|
1064
|
+
workflow_id: impl Into<String>,
|
|
1065
|
+
run_id: impl Into<String>,
|
|
1066
|
+
) -> UntypedWorkflowHandle<Self::RawClientT>
|
|
1067
|
+
where
|
|
1068
|
+
Self::RawClientT: Clone,
|
|
1069
|
+
{
|
|
1070
|
+
let rid = run_id.into();
|
|
1071
|
+
UntypedWorkflowHandle::new(
|
|
1072
|
+
self.wf_svc(),
|
|
1073
|
+
WorkflowExecutionInfo {
|
|
1074
|
+
namespace: self.namespace().to_string(),
|
|
1075
|
+
workflow_id: workflow_id.into(),
|
|
1076
|
+
run_id: if rid.is_empty() { None } else { Some(rid) },
|
|
1077
|
+
},
|
|
1078
|
+
)
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
impl<T> WfClientExt for T where T: WfHandleClient + Sized {}
|
|
@@ -15,7 +15,7 @@ use tonic::{body::BoxBody, client::GrpcService, metadata::KeyAndValueRef};
|
|
|
15
15
|
|
|
16
16
|
pub(super) mod sealed {
|
|
17
17
|
use super::*;
|
|
18
|
-
use crate::{ConfiguredClient, RetryClient};
|
|
18
|
+
use crate::{Client, ConfiguredClient, InterceptedMetricsSvc, RetryClient};
|
|
19
19
|
use futures::TryFutureExt;
|
|
20
20
|
use tonic::{Request, Response, Status};
|
|
21
21
|
|
|
@@ -104,6 +104,14 @@ pub(super) mod sealed {
|
|
|
104
104
|
&mut self.client
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
|
|
108
|
+
impl RawClientLike for Client {
|
|
109
|
+
type SvcType = InterceptedMetricsSvc;
|
|
110
|
+
|
|
111
|
+
fn client(&mut self) -> &mut WorkflowServiceClient<Self::SvcType> {
|
|
112
|
+
&mut self.inner
|
|
113
|
+
}
|
|
114
|
+
}
|
|
107
115
|
}
|
|
108
116
|
|
|
109
117
|
/// Helper for cloning a tonic request as long as the inner message may be cloned.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
-
ClientOptions, Result, RetryConfig, WorkflowClientTrait, WorkflowOptions,
|
|
2
|
+
ClientOptions, RawClientLikeUser, Result, RetryConfig, WorkflowClientTrait, WorkflowOptions,
|
|
3
3
|
WorkflowTaskCompletion,
|
|
4
4
|
};
|
|
5
5
|
use backoff::{backoff::Backoff, ExponentialBackoff};
|
|
@@ -592,3 +592,14 @@ mod tests {
|
|
|
592
592
|
}
|
|
593
593
|
}
|
|
594
594
|
}
|
|
595
|
+
|
|
596
|
+
impl<C> RawClientLikeUser for RetryClient<C>
|
|
597
|
+
where
|
|
598
|
+
C: RawClientLikeUser,
|
|
599
|
+
{
|
|
600
|
+
type RawClientT = C::RawClientT;
|
|
601
|
+
|
|
602
|
+
fn wf_svc(&self) -> Self::RawClientT {
|
|
603
|
+
self.client.wf_svc()
|
|
604
|
+
}
|
|
605
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
use crate::{InterceptedMetricsSvc, RawClientLike};
|
|
2
|
+
use anyhow::{anyhow, bail};
|
|
3
|
+
use std::marker::PhantomData;
|
|
4
|
+
use temporal_sdk_core_protos::{
|
|
5
|
+
coresdk::{common::Payload, FromPayloadsExt},
|
|
6
|
+
temporal::api::{
|
|
7
|
+
common::v1::WorkflowExecution, enums::v1::HistoryEventFilterType, failure::v1::Failure,
|
|
8
|
+
history::v1::history_event::Attributes,
|
|
9
|
+
workflowservice::v1::GetWorkflowExecutionHistoryRequest,
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/// Enumerates terminal states for a particular workflow execution
|
|
14
|
+
// TODO: Add non-proto failure types, flesh out details, etc.
|
|
15
|
+
#[derive(Debug)]
|
|
16
|
+
#[allow(clippy::large_enum_variant)]
|
|
17
|
+
pub enum WorkflowExecutionResult<T> {
|
|
18
|
+
/// The workflow finished successfully
|
|
19
|
+
Succeeded(T),
|
|
20
|
+
/// The workflow finished in failure
|
|
21
|
+
Failed(Failure),
|
|
22
|
+
/// The workflow was cancelled
|
|
23
|
+
Cancelled(Vec<Payload>),
|
|
24
|
+
/// The workflow was terminated
|
|
25
|
+
Terminated(Vec<Payload>),
|
|
26
|
+
/// The workflow timed out
|
|
27
|
+
TimedOut,
|
|
28
|
+
/// The workflow continued as new
|
|
29
|
+
ContinuedAsNew,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Options for fetching workflow results
|
|
33
|
+
#[derive(Debug, Clone, Copy)]
|
|
34
|
+
pub struct GetWorkflowResultOpts {
|
|
35
|
+
/// If true (the default), follows to the next workflow run in the execution chain while
|
|
36
|
+
/// retrieving results.
|
|
37
|
+
pub follow_runs: bool,
|
|
38
|
+
}
|
|
39
|
+
impl Default for GetWorkflowResultOpts {
|
|
40
|
+
fn default() -> Self {
|
|
41
|
+
Self { follow_runs: true }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// A workflow handle which can refer to a specific workflow run, or a chain of workflow runs with
|
|
46
|
+
/// the same workflow id.
|
|
47
|
+
pub struct WorkflowHandle<ClientT, ResultT> {
|
|
48
|
+
client: ClientT,
|
|
49
|
+
info: WorkflowExecutionInfo,
|
|
50
|
+
|
|
51
|
+
_res_type: PhantomData<ResultT>,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Holds needed information to refer to a specific workflow run, or workflow execution chain
|
|
55
|
+
pub struct WorkflowExecutionInfo {
|
|
56
|
+
/// Namespace the workflow lives in
|
|
57
|
+
pub namespace: String,
|
|
58
|
+
/// The workflow's id
|
|
59
|
+
pub workflow_id: String,
|
|
60
|
+
/// If set, target this specific run of the workflow
|
|
61
|
+
pub run_id: Option<String>,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
impl WorkflowExecutionInfo {
|
|
65
|
+
/// Bind the workflow info to a specific client, turning it into a workflow handle
|
|
66
|
+
pub fn bind_untyped<CT>(self, client: CT) -> UntypedWorkflowHandle<CT>
|
|
67
|
+
where
|
|
68
|
+
CT: RawClientLike<SvcType = InterceptedMetricsSvc> + Clone,
|
|
69
|
+
{
|
|
70
|
+
UntypedWorkflowHandle::new(client, self)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/// A workflow handle to a workflow with unknown types. Uses raw payloads.
|
|
75
|
+
pub type UntypedWorkflowHandle<CT> = WorkflowHandle<CT, Vec<Payload>>;
|
|
76
|
+
|
|
77
|
+
impl<CT, RT> WorkflowHandle<CT, RT>
|
|
78
|
+
where
|
|
79
|
+
CT: RawClientLike<SvcType = InterceptedMetricsSvc> + Clone,
|
|
80
|
+
// TODO: Make more generic, capable of (de)serialization w/ serde
|
|
81
|
+
RT: FromPayloadsExt,
|
|
82
|
+
{
|
|
83
|
+
pub(crate) fn new(client: CT, info: WorkflowExecutionInfo) -> Self {
|
|
84
|
+
Self {
|
|
85
|
+
client,
|
|
86
|
+
info,
|
|
87
|
+
_res_type: PhantomData::<RT>,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub async fn get_workflow_result(
|
|
92
|
+
&self,
|
|
93
|
+
opts: GetWorkflowResultOpts,
|
|
94
|
+
) -> Result<WorkflowExecutionResult<RT>, anyhow::Error> {
|
|
95
|
+
let mut next_page_tok = vec![];
|
|
96
|
+
let mut run_id = self.info.run_id.clone().unwrap_or_default();
|
|
97
|
+
loop {
|
|
98
|
+
let server_res = self
|
|
99
|
+
.client
|
|
100
|
+
.clone()
|
|
101
|
+
.client()
|
|
102
|
+
.get_workflow_execution_history(GetWorkflowExecutionHistoryRequest {
|
|
103
|
+
namespace: self.info.namespace.to_string(),
|
|
104
|
+
execution: Some(WorkflowExecution {
|
|
105
|
+
workflow_id: self.info.workflow_id.clone(),
|
|
106
|
+
run_id: run_id.clone(),
|
|
107
|
+
}),
|
|
108
|
+
skip_archival: true,
|
|
109
|
+
wait_new_event: true,
|
|
110
|
+
history_event_filter_type: HistoryEventFilterType::CloseEvent as i32,
|
|
111
|
+
next_page_token: next_page_tok.clone(),
|
|
112
|
+
..Default::default()
|
|
113
|
+
})
|
|
114
|
+
.await?
|
|
115
|
+
.into_inner();
|
|
116
|
+
|
|
117
|
+
let mut history = server_res
|
|
118
|
+
.history
|
|
119
|
+
.ok_or_else(|| anyhow!("Server returned an empty history!"))?;
|
|
120
|
+
|
|
121
|
+
if history.events.is_empty() {
|
|
122
|
+
next_page_tok = server_res.next_page_token;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// If page token was previously set, clear it.
|
|
126
|
+
next_page_tok = vec![];
|
|
127
|
+
|
|
128
|
+
let event_attrs = history.events.pop().and_then(|ev| ev.attributes);
|
|
129
|
+
|
|
130
|
+
macro_rules! follow {
|
|
131
|
+
($attrs:ident) => {
|
|
132
|
+
if opts.follow_runs && $attrs.new_execution_run_id != "" {
|
|
133
|
+
run_id = $attrs.new_execution_run_id;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
break match event_attrs {
|
|
140
|
+
Some(Attributes::WorkflowExecutionCompletedEventAttributes(attrs)) => {
|
|
141
|
+
follow!(attrs);
|
|
142
|
+
Ok(WorkflowExecutionResult::Succeeded(RT::from_payloads(
|
|
143
|
+
attrs.result,
|
|
144
|
+
)))
|
|
145
|
+
}
|
|
146
|
+
Some(Attributes::WorkflowExecutionFailedEventAttributes(attrs)) => {
|
|
147
|
+
follow!(attrs);
|
|
148
|
+
Ok(WorkflowExecutionResult::Failed(
|
|
149
|
+
attrs.failure.unwrap_or_default(),
|
|
150
|
+
))
|
|
151
|
+
}
|
|
152
|
+
Some(Attributes::WorkflowExecutionCanceledEventAttributes(attrs)) => Ok(
|
|
153
|
+
WorkflowExecutionResult::Cancelled(Vec::from_payloads(attrs.details)),
|
|
154
|
+
),
|
|
155
|
+
Some(Attributes::WorkflowExecutionTimedOutEventAttributes(attrs)) => {
|
|
156
|
+
follow!(attrs);
|
|
157
|
+
Ok(WorkflowExecutionResult::TimedOut)
|
|
158
|
+
}
|
|
159
|
+
Some(Attributes::WorkflowExecutionTerminatedEventAttributes(attrs)) => Ok(
|
|
160
|
+
WorkflowExecutionResult::Terminated(Vec::from_payloads(attrs.details)),
|
|
161
|
+
),
|
|
162
|
+
Some(Attributes::WorkflowExecutionContinuedAsNewEventAttributes(attrs)) => {
|
|
163
|
+
if opts.follow_runs {
|
|
164
|
+
if !attrs.new_execution_run_id.is_empty() {
|
|
165
|
+
run_id = attrs.new_execution_run_id;
|
|
166
|
+
continue;
|
|
167
|
+
} else {
|
|
168
|
+
bail!("New execution run id was empty in continue as new event!");
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
Ok(WorkflowExecutionResult::ContinuedAsNew)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
o => Err(anyhow!(
|
|
175
|
+
"Server returned an event that didn't match the CloseEvent filter. \
|
|
176
|
+
This is either a server bug or a new event the SDK does not understand. \
|
|
177
|
+
Event details: {:?}",
|
|
178
|
+
o
|
|
179
|
+
)),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
replay::DEFAULT_WORKFLOW_TYPE,
|
|
3
|
-
test_help::{
|
|
4
|
-
build_mock_pollers, canned_histories, mock_worker, MockPollCfg, ResponseType, TEST_Q,
|
|
5
|
-
},
|
|
3
|
+
test_help::{canned_histories, mock_sdk, MockPollCfg, ResponseType},
|
|
6
4
|
worker::client::mocks::mock_workflow_client,
|
|
7
5
|
workflow::managed_wf::ManagedWFFunc,
|
|
8
6
|
};
|
|
9
|
-
use std::sync::Arc;
|
|
10
7
|
use temporal_client::WorkflowOptions;
|
|
11
8
|
use temporal_sdk::{ChildWorkflowOptions, Signal, WfContext, WorkflowFunction, WorkflowResult};
|
|
12
9
|
use temporal_sdk_core_protos::coresdk::child_workflow::{
|
|
13
10
|
child_workflow_result, ChildWorkflowCancellationType,
|
|
14
11
|
};
|
|
15
|
-
use temporal_sdk_core_test_utils::TestWorker;
|
|
16
12
|
use tokio::join;
|
|
17
13
|
|
|
18
14
|
const SIGNAME: &str = "SIGNAME";
|
|
@@ -26,10 +22,12 @@ async fn signal_child_workflow(#[case] serial: bool) {
|
|
|
26
22
|
let wf_type = DEFAULT_WORKFLOW_TYPE;
|
|
27
23
|
let t = canned_histories::single_child_workflow_signaled("child-id-1", SIGNAME);
|
|
28
24
|
let mock = mock_workflow_client();
|
|
29
|
-
let
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
let mut worker = mock_sdk(MockPollCfg::from_resp_batches(
|
|
26
|
+
wf_id,
|
|
27
|
+
t,
|
|
28
|
+
[ResponseType::AllHistory],
|
|
29
|
+
mock,
|
|
30
|
+
));
|
|
33
31
|
|
|
34
32
|
let wf = move |ctx: WfContext| async move {
|
|
35
33
|
let child = ctx.child_workflow(ChildWorkflowOptions {
|
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
replay::DEFAULT_WORKFLOW_TYPE,
|
|
3
|
-
test_help::{
|
|
4
|
-
build_mock_pollers, canned_histories, mock_worker, MockPollCfg, ResponseType, TEST_Q,
|
|
5
|
-
},
|
|
3
|
+
test_help::{canned_histories, mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
|
|
6
4
|
worker::client::mocks::mock_workflow_client,
|
|
7
5
|
};
|
|
8
6
|
use std::{
|
|
9
|
-
sync::{
|
|
10
|
-
atomic::{AtomicBool, AtomicUsize, Ordering},
|
|
11
|
-
Arc,
|
|
12
|
-
},
|
|
7
|
+
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
|
13
8
|
time::Duration,
|
|
14
9
|
};
|
|
15
10
|
use temporal_client::WorkflowOptions;
|
|
16
11
|
use temporal_sdk::{WfContext, WorkflowResult};
|
|
17
12
|
use temporal_sdk_core_protos::temporal::api::enums::v1::WorkflowTaskFailedCause;
|
|
18
|
-
use temporal_sdk_core_test_utils::TestWorker;
|
|
19
13
|
|
|
20
14
|
static DID_FAIL: AtomicBool = AtomicBool::new(false);
|
|
21
15
|
pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
|
|
@@ -32,7 +26,7 @@ pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
|
|
|
32
26
|
/// Verifies that workflow panics (which in this case the Rust SDK turns into workflow activation
|
|
33
27
|
/// failures) are turned into unspecified WFT failures.
|
|
34
28
|
#[tokio::test]
|
|
35
|
-
async fn
|
|
29
|
+
async fn test_panic_wf_task_rejected_properly() {
|
|
36
30
|
let wf_id = "fakeid";
|
|
37
31
|
let wf_type = DEFAULT_WORKFLOW_TYPE;
|
|
38
32
|
let t = canned_histories::workflow_fails_with_failure_after_timer("1");
|
|
@@ -43,9 +37,7 @@ async fn test_wf_task_rejected_properly() {
|
|
|
43
37
|
mh.num_expected_fails = Some(1);
|
|
44
38
|
mh.expect_fail_wft_matcher =
|
|
45
39
|
Box::new(|_, cause, _| matches!(cause, WorkflowTaskFailedCause::Unspecified));
|
|
46
|
-
let
|
|
47
|
-
let core = mock_worker(mock);
|
|
48
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
40
|
+
let mut worker = mock_sdk(mh);
|
|
49
41
|
|
|
50
42
|
worker.register_wf(wf_type.to_owned(), timer_wf_fails_once);
|
|
51
43
|
worker
|
|
@@ -82,14 +74,11 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
|
|
|
82
74
|
mh.num_expected_fails = Some(1);
|
|
83
75
|
mh.expect_fail_wft_matcher =
|
|
84
76
|
Box::new(|_, cause, _| matches!(cause, WorkflowTaskFailedCause::NonDeterministicError));
|
|
85
|
-
let mut
|
|
86
|
-
|
|
87
|
-
mock.worker_cfg(|cfg| {
|
|
77
|
+
let mut worker = mock_sdk_cfg(mh, |cfg| {
|
|
78
|
+
if use_cache {
|
|
88
79
|
cfg.max_cached_workflows = 2;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
let core = mock_worker(mock);
|
|
92
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
80
|
+
}
|
|
81
|
+
});
|
|
93
82
|
|
|
94
83
|
let started_count: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
|
|
95
84
|
worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
|
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
replay::{default_wes_attribs, TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE},
|
|
3
|
-
test_help::{
|
|
3
|
+
test_help::{mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
|
|
4
4
|
worker::client::mocks::mock_workflow_client,
|
|
5
5
|
};
|
|
6
6
|
use anyhow::anyhow;
|
|
7
7
|
use futures::future::join_all;
|
|
8
8
|
use std::{
|
|
9
|
-
sync::{
|
|
10
|
-
atomic::{AtomicUsize, Ordering},
|
|
11
|
-
Arc,
|
|
12
|
-
},
|
|
9
|
+
sync::atomic::{AtomicUsize, Ordering},
|
|
13
10
|
time::Duration,
|
|
14
11
|
};
|
|
15
12
|
use temporal_client::WorkflowOptions;
|
|
16
|
-
use temporal_sdk::{LocalActivityOptions, WfContext, WorkflowResult};
|
|
13
|
+
use temporal_sdk::{ActContext, LocalActivityOptions, WfContext, WorkflowResult};
|
|
17
14
|
use temporal_sdk_core_protos::{
|
|
18
15
|
coresdk::{common::RetryPolicy, AsJsonPayloadExt},
|
|
19
16
|
temporal::api::{enums::v1::EventType, failure::v1::Failure},
|
|
20
17
|
};
|
|
21
|
-
use temporal_sdk_core_test_utils::TestWorker;
|
|
22
18
|
use tokio::sync::Barrier;
|
|
23
19
|
|
|
24
|
-
async fn echo(e: String) -> anyhow::Result<String> {
|
|
20
|
+
async fn echo(_ctx: ActContext, e: String) -> anyhow::Result<String> {
|
|
25
21
|
Ok(e)
|
|
26
22
|
}
|
|
27
23
|
|
|
@@ -52,12 +48,11 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
|
|
|
52
48
|
vec![1.into(), 2.into(), ResponseType::AllHistory]
|
|
53
49
|
};
|
|
54
50
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, resps, mock);
|
|
55
|
-
let mut
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
51
|
+
let mut worker = mock_sdk_cfg(mh, |cfg| {
|
|
52
|
+
if cached {
|
|
53
|
+
cfg.max_cached_workflows = 1;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
61
56
|
|
|
62
57
|
worker.register_wf(
|
|
63
58
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
@@ -119,12 +114,13 @@ async fn local_act_many_concurrent() {
|
|
|
119
114
|
let wf_id = "fakeid";
|
|
120
115
|
let mock = mock_workflow_client();
|
|
121
116
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
|
|
122
|
-
let
|
|
123
|
-
let core = mock_worker(mock);
|
|
124
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
117
|
+
let mut worker = mock_sdk(mh);
|
|
125
118
|
|
|
126
119
|
worker.register_wf(DEFAULT_WORKFLOW_TYPE.to_owned(), local_act_fanout_wf);
|
|
127
|
-
worker.register_activity(
|
|
120
|
+
worker.register_activity(
|
|
121
|
+
"echo",
|
|
122
|
+
|_ctx: ActContext, str: String| async move { Ok(str) },
|
|
123
|
+
);
|
|
128
124
|
worker
|
|
129
125
|
.submit_wf(
|
|
130
126
|
wf_id.to_owned(),
|
|
@@ -167,10 +163,9 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
167
163
|
// and might poll an extra time
|
|
168
164
|
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 2, 2, 2], mock);
|
|
169
165
|
mh.enforce_correct_number_of_polls = false;
|
|
170
|
-
let mut
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
let mut worker = TestWorker::new(core.clone(), TEST_Q.to_string());
|
|
166
|
+
let mut worker = mock_sdk_cfg(mh, |wc| wc.max_cached_workflows = 1);
|
|
167
|
+
let core = worker.orig_core_worker.clone();
|
|
168
|
+
|
|
174
169
|
let shutdown_barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
|
|
175
170
|
|
|
176
171
|
worker.register_wf(
|
|
@@ -185,7 +180,7 @@ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
|
|
|
185
180
|
Ok(().into())
|
|
186
181
|
},
|
|
187
182
|
);
|
|
188
|
-
worker.register_activity("echo", move |str: String| async move {
|
|
183
|
+
worker.register_activity("echo", move |_ctx: ActContext, str: String| async move {
|
|
189
184
|
if shutdown_middle {
|
|
190
185
|
shutdown_barr.wait().await;
|
|
191
186
|
}
|
|
@@ -226,9 +221,7 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
|
226
221
|
let wf_id = "fakeid";
|
|
227
222
|
let mock = mock_workflow_client();
|
|
228
223
|
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
|
|
229
|
-
let
|
|
230
|
-
let core = mock_worker(mock);
|
|
231
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
224
|
+
let mut worker = mock_sdk(mh);
|
|
232
225
|
|
|
233
226
|
worker.register_wf(
|
|
234
227
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
@@ -256,7 +249,7 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
|
256
249
|
},
|
|
257
250
|
);
|
|
258
251
|
let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
|
|
259
|
-
worker.register_activity("echo", move |_: String| async move {
|
|
252
|
+
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
260
253
|
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
261
254
|
if 2 == attempts.fetch_add(1, Ordering::Relaxed) && eventually_pass {
|
|
262
255
|
Ok(())
|
|
@@ -309,10 +302,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
309
302
|
[1.into(), 2.into(), ResponseType::AllHistory],
|
|
310
303
|
mock,
|
|
311
304
|
);
|
|
312
|
-
let mut
|
|
313
|
-
mock.worker_cfg(|w| w.max_cached_workflows = 1);
|
|
314
|
-
let core = mock_worker(mock);
|
|
315
|
-
let mut worker = TestWorker::new(Arc::new(core), TEST_Q.to_string());
|
|
305
|
+
let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
|
|
316
306
|
|
|
317
307
|
worker.register_wf(
|
|
318
308
|
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
@@ -338,7 +328,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
338
328
|
Ok(().into())
|
|
339
329
|
},
|
|
340
330
|
);
|
|
341
|
-
worker.register_activity("echo", move |_: String| async move {
|
|
331
|
+
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
342
332
|
Result::<(), _>::Err(anyhow!("Oh no I failed!"))
|
|
343
333
|
});
|
|
344
334
|
worker
|