@temporalio/core-bridge 1.1.0 → 1.3.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 +786 -54
- package/Cargo.toml +2 -2
- package/common.js +7 -3
- package/index.d.ts +110 -3
- package/index.js +2 -6
- 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/scripts/build.js +4 -3
- package/sdk-core/.buildkite/docker/Dockerfile +2 -1
- package/sdk-core/ARCHITECTURE.md +2 -2
- package/sdk-core/README.md +12 -0
- package/sdk-core/bridge-ffi/Cargo.toml +2 -2
- package/sdk-core/client/Cargo.toml +6 -4
- package/sdk-core/client/src/lib.rs +338 -215
- package/sdk-core/client/src/raw.rs +352 -106
- package/sdk-core/client/src/retry.rs +159 -133
- package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
- package/sdk-core/core/Cargo.toml +18 -9
- package/sdk-core/core/src/core_tests/activity_tasks.rs +63 -23
- package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
- package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
- package/sdk-core/core/src/core_tests/workers.rs +3 -2
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
- package/sdk-core/core/src/ephemeral_server/mod.rs +499 -0
- package/sdk-core/core/src/lib.rs +60 -26
- package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
- package/sdk-core/core/src/replay/mod.rs +3 -3
- package/sdk-core/core/src/retry_logic.rs +10 -9
- package/sdk-core/core/src/telemetry/mod.rs +10 -7
- package/sdk-core/core/src/test_help/mod.rs +18 -8
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
- package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
- package/sdk-core/core/src/worker/activities.rs +6 -12
- package/sdk-core/core/src/worker/client.rs +193 -64
- package/sdk-core/core/src/worker/mod.rs +14 -19
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
- package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
- package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
- package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
- package/sdk-core/core/src/worker/workflow/mod.rs +59 -58
- package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
- package/sdk-core/core-api/Cargo.toml +2 -2
- package/sdk-core/core-api/src/errors.rs +3 -11
- package/sdk-core/core-api/src/worker.rs +7 -0
- package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
- package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
- package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
- package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
- package/sdk-core/protos/api_upstream/Makefile +2 -2
- package/sdk-core/protos/api_upstream/buf.yaml +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
- package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
- package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
- package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
- package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
- package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
- package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
- package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
- package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
- package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
- package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
- package/sdk-core/sdk/Cargo.toml +2 -2
- package/sdk-core/sdk/src/lib.rs +2 -2
- package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
- package/sdk-core/sdk/src/workflow_context.rs +30 -6
- package/sdk-core/sdk/src/workflow_future.rs +4 -4
- package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
- package/sdk-core/sdk-core-protos/build.rs +9 -1
- package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
- package/sdk-core/test-utils/Cargo.toml +3 -3
- package/sdk-core/test-utils/src/canned_histories.rs +58 -0
- package/sdk-core/test-utils/src/lib.rs +14 -10
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +141 -0
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
- package/sdk-core/tests/integ_tests/polling_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/queries_tests.rs +4 -4
- package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
- package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
- package/sdk-core/tests/load_tests.rs +2 -1
- package/sdk-core/tests/main.rs +10 -0
- package/src/conversions.rs +138 -91
- package/src/helpers.rs +190 -0
- package/src/lib.rs +10 -912
- package/src/runtime.rs +436 -0
- package/src/testing.rs +67 -0
- package/src/worker.rs +465 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
use std::time::{SystemTime, UNIX_EPOCH};
|
|
2
|
+
use temporal_client::{TestService, WorkflowService};
|
|
3
|
+
use temporal_sdk_core::{
|
|
4
|
+
ephemeral_server::{
|
|
5
|
+
EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporaliteConfigBuilder,
|
|
6
|
+
TestServerConfigBuilder,
|
|
7
|
+
},
|
|
8
|
+
ClientOptions, ClientOptionsBuilder,
|
|
9
|
+
};
|
|
10
|
+
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::DescribeNamespaceRequest;
|
|
11
|
+
use temporal_sdk_core_test_utils::NAMESPACE;
|
|
12
|
+
use url::Url;
|
|
13
|
+
|
|
14
|
+
#[tokio::test]
|
|
15
|
+
async fn temporalite_default() {
|
|
16
|
+
let config = TemporaliteConfigBuilder::default()
|
|
17
|
+
.exe(default_cached_download())
|
|
18
|
+
.build()
|
|
19
|
+
.unwrap();
|
|
20
|
+
let server = config.start_server().await.unwrap();
|
|
21
|
+
assert_ephemeral_server(&server).await;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[tokio::test]
|
|
25
|
+
async fn temporalite_fixed() {
|
|
26
|
+
let config = TemporaliteConfigBuilder::default()
|
|
27
|
+
.exe(fixed_cached_download("v0.1.1"))
|
|
28
|
+
.build()
|
|
29
|
+
.unwrap();
|
|
30
|
+
let server = config.start_server().await.unwrap();
|
|
31
|
+
assert_ephemeral_server(&server).await;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[tokio::test]
|
|
35
|
+
async fn temporalite_shutdown_port_reuse() {
|
|
36
|
+
// Start, test shutdown, do again immediately on same port to ensure we can
|
|
37
|
+
// reuse after shutdown
|
|
38
|
+
let config = TemporaliteConfigBuilder::default()
|
|
39
|
+
.exe(default_cached_download())
|
|
40
|
+
.port(Some(10123))
|
|
41
|
+
.build()
|
|
42
|
+
.unwrap();
|
|
43
|
+
let mut server = config.start_server().await.unwrap();
|
|
44
|
+
assert_ephemeral_server(&server).await;
|
|
45
|
+
server.shutdown().await.unwrap();
|
|
46
|
+
let mut server = config.start_server().await.unwrap();
|
|
47
|
+
assert_ephemeral_server(&server).await;
|
|
48
|
+
server.shutdown().await.unwrap();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[tokio::test]
|
|
52
|
+
async fn test_server_default() {
|
|
53
|
+
let config = TestServerConfigBuilder::default()
|
|
54
|
+
.exe(default_cached_download())
|
|
55
|
+
.build()
|
|
56
|
+
.unwrap();
|
|
57
|
+
let server = config.start_server().await.unwrap();
|
|
58
|
+
assert_ephemeral_server(&server).await;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#[tokio::test]
|
|
62
|
+
async fn test_server_fixed() {
|
|
63
|
+
let config = TestServerConfigBuilder::default()
|
|
64
|
+
.exe(fixed_cached_download("v1.16.0"))
|
|
65
|
+
.build()
|
|
66
|
+
.unwrap();
|
|
67
|
+
let server = config.start_server().await.unwrap();
|
|
68
|
+
assert_ephemeral_server(&server).await;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#[tokio::test]
|
|
72
|
+
async fn test_server_shutdown_port_reuse() {
|
|
73
|
+
// Start, test shutdown, do again immediately on same port to ensure we can
|
|
74
|
+
// reuse after shutdown
|
|
75
|
+
let config = TestServerConfigBuilder::default()
|
|
76
|
+
.exe(default_cached_download())
|
|
77
|
+
.port(Some(10124))
|
|
78
|
+
.build()
|
|
79
|
+
.unwrap();
|
|
80
|
+
let mut server = config.start_server().await.unwrap();
|
|
81
|
+
assert_ephemeral_server(&server).await;
|
|
82
|
+
server.shutdown().await.unwrap();
|
|
83
|
+
let mut server = config.start_server().await.unwrap();
|
|
84
|
+
assert_ephemeral_server(&server).await;
|
|
85
|
+
server.shutdown().await.unwrap();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async fn assert_ephemeral_server(server: &EphemeralServer) {
|
|
89
|
+
// Connect and describe namespace
|
|
90
|
+
let mut client = client_options(&server.target)
|
|
91
|
+
.connect_no_namespace(None, None)
|
|
92
|
+
.await
|
|
93
|
+
.unwrap();
|
|
94
|
+
let resp = client
|
|
95
|
+
.describe_namespace(DescribeNamespaceRequest {
|
|
96
|
+
namespace: NAMESPACE.to_string(),
|
|
97
|
+
..Default::default()
|
|
98
|
+
})
|
|
99
|
+
.await
|
|
100
|
+
.unwrap();
|
|
101
|
+
assert!(resp.into_inner().namespace_info.unwrap().name == "default");
|
|
102
|
+
|
|
103
|
+
// If it has test service, make sure we can use it too
|
|
104
|
+
if server.has_test_service {
|
|
105
|
+
let resp = client.get_current_time(()).await.unwrap();
|
|
106
|
+
// Make sure it's within 5 mins of now
|
|
107
|
+
let resp_seconds = resp.get_ref().time.as_ref().unwrap().seconds as u64;
|
|
108
|
+
let curr_seconds = SystemTime::now()
|
|
109
|
+
.duration_since(UNIX_EPOCH)
|
|
110
|
+
.unwrap()
|
|
111
|
+
.as_secs();
|
|
112
|
+
assert!(curr_seconds - 300 < resp_seconds && curr_seconds + 300 > resp_seconds);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn default_cached_download() -> EphemeralExe {
|
|
117
|
+
EphemeralExe::CachedDownload {
|
|
118
|
+
version: EphemeralExeVersion::Default {
|
|
119
|
+
sdk_name: "sdk-rust".to_string(),
|
|
120
|
+
sdk_version: "0.1.0".to_string(),
|
|
121
|
+
},
|
|
122
|
+
dest_dir: None,
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
fn fixed_cached_download(version: &str) -> EphemeralExe {
|
|
127
|
+
EphemeralExe::CachedDownload {
|
|
128
|
+
version: EphemeralExeVersion::Fixed(version.to_string()),
|
|
129
|
+
dest_dir: None,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fn client_options(target: &str) -> ClientOptions {
|
|
134
|
+
return ClientOptionsBuilder::default()
|
|
135
|
+
.identity("integ_tester".to_string())
|
|
136
|
+
.target_url(Url::try_from(&*format!("http://{}", target)).unwrap())
|
|
137
|
+
.client_name("temporal-core".to_string())
|
|
138
|
+
.client_version("0.1.0".to_string())
|
|
139
|
+
.build()
|
|
140
|
+
.unwrap();
|
|
141
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
use assert_matches::assert_matches;
|
|
2
2
|
use std::time::Duration;
|
|
3
|
+
use temporal_client::{WfClientExt, WorkflowOptions};
|
|
4
|
+
use temporal_sdk::{ActContext, ActivityOptions, WfContext};
|
|
3
5
|
use temporal_sdk_core_protos::{
|
|
4
6
|
coresdk::{
|
|
5
7
|
activity_result::{
|
|
@@ -9,12 +11,12 @@ use temporal_sdk_core_protos::{
|
|
|
9
11
|
workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
|
|
10
12
|
workflow_commands::{ActivityCancellationType, ScheduleActivity},
|
|
11
13
|
workflow_completion::WorkflowActivationCompletion,
|
|
12
|
-
ActivityHeartbeat, ActivityTaskCompletion, IntoCompletion,
|
|
14
|
+
ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt, IntoCompletion,
|
|
13
15
|
},
|
|
14
16
|
temporal::api::common::v1::{Payload, RetryPolicy},
|
|
15
17
|
};
|
|
16
18
|
use temporal_sdk_core_test_utils::{
|
|
17
|
-
init_core_and_create_wf, schedule_activity_cmd, WorkerTestHelpers,
|
|
19
|
+
init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter, WorkerTestHelpers,
|
|
18
20
|
};
|
|
19
21
|
use tokio::time::sleep;
|
|
20
22
|
|
|
@@ -103,14 +105,14 @@ async fn many_act_fails_with_heartbeats() {
|
|
|
103
105
|
activity_id: activity_id.to_string(),
|
|
104
106
|
activity_type: "test_act".to_string(),
|
|
105
107
|
task_queue: starter.get_task_queue().to_string(),
|
|
106
|
-
start_to_close_timeout: Some(
|
|
108
|
+
start_to_close_timeout: Some(prost_dur!(from_secs(30))),
|
|
107
109
|
retry_policy: Some(RetryPolicy {
|
|
108
|
-
initial_interval: Some(
|
|
110
|
+
initial_interval: Some(prost_dur!(from_millis(10))),
|
|
109
111
|
backoff_coefficient: 1.0,
|
|
110
112
|
maximum_attempts: 4,
|
|
111
113
|
..Default::default()
|
|
112
114
|
}),
|
|
113
|
-
heartbeat_timeout: Some(
|
|
115
|
+
heartbeat_timeout: Some(prost_dur!(from_secs(1))),
|
|
114
116
|
..Default::default()
|
|
115
117
|
}
|
|
116
118
|
.into(),
|
|
@@ -166,3 +168,51 @@ async fn many_act_fails_with_heartbeats() {
|
|
|
166
168
|
core.complete_execution(&task.run_id).await;
|
|
167
169
|
core.shutdown().await;
|
|
168
170
|
}
|
|
171
|
+
|
|
172
|
+
#[tokio::test]
|
|
173
|
+
async fn activity_doesnt_heartbeat_hits_timeout_then_completes() {
|
|
174
|
+
let wf_name = "activity_doesnt_heartbeat_hits_timeout_then_completes";
|
|
175
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
176
|
+
let mut worker = starter.worker().await;
|
|
177
|
+
let client = starter.get_client().await;
|
|
178
|
+
worker.register_activity(
|
|
179
|
+
"echo_activity",
|
|
180
|
+
|_ctx: ActContext, echo_me: String| async move {
|
|
181
|
+
sleep(Duration::from_secs(4)).await;
|
|
182
|
+
Ok(echo_me)
|
|
183
|
+
},
|
|
184
|
+
);
|
|
185
|
+
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
186
|
+
let res = ctx
|
|
187
|
+
.activity(ActivityOptions {
|
|
188
|
+
activity_type: "echo_activity".to_string(),
|
|
189
|
+
input: "hi!".as_json_payload().expect("serializes fine"),
|
|
190
|
+
start_to_close_timeout: Some(Duration::from_secs(10)),
|
|
191
|
+
heartbeat_timeout: Some(Duration::from_secs(2)),
|
|
192
|
+
retry_policy: Some(RetryPolicy {
|
|
193
|
+
maximum_attempts: 1,
|
|
194
|
+
..Default::default()
|
|
195
|
+
}),
|
|
196
|
+
..Default::default()
|
|
197
|
+
})
|
|
198
|
+
.await;
|
|
199
|
+
assert!(res.timed_out());
|
|
200
|
+
Ok(().into())
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
let run_id = worker
|
|
204
|
+
.submit_wf(
|
|
205
|
+
wf_name.to_owned(),
|
|
206
|
+
wf_name.to_owned(),
|
|
207
|
+
vec![],
|
|
208
|
+
WorkflowOptions::default(),
|
|
209
|
+
)
|
|
210
|
+
.await
|
|
211
|
+
.unwrap();
|
|
212
|
+
worker.run_until_done().await.unwrap();
|
|
213
|
+
let handle = client.get_untyped_workflow_handle(wf_name, run_id);
|
|
214
|
+
handle
|
|
215
|
+
.get_workflow_result(Default::default())
|
|
216
|
+
.await
|
|
217
|
+
.unwrap();
|
|
218
|
+
}
|
|
@@ -24,12 +24,12 @@ async fn simple_query_legacy() {
|
|
|
24
24
|
vec![
|
|
25
25
|
StartTimer {
|
|
26
26
|
seq: 0,
|
|
27
|
-
start_to_fire_timeout: Some(
|
|
27
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(500))),
|
|
28
28
|
}
|
|
29
29
|
.into(),
|
|
30
30
|
StartTimer {
|
|
31
31
|
seq: 1,
|
|
32
|
-
start_to_fire_timeout: Some(
|
|
32
|
+
start_to_fire_timeout: Some(prost_dur!(from_secs(3))),
|
|
33
33
|
}
|
|
34
34
|
.into(),
|
|
35
35
|
],
|
|
@@ -343,12 +343,12 @@ async fn fail_legacy_query() {
|
|
|
343
343
|
let t1_resp = vec![
|
|
344
344
|
StartTimer {
|
|
345
345
|
seq: 1,
|
|
346
|
-
start_to_fire_timeout: Some(
|
|
346
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(500))),
|
|
347
347
|
}
|
|
348
348
|
.into(),
|
|
349
349
|
StartTimer {
|
|
350
350
|
seq: 2,
|
|
351
|
-
start_to_fire_timeout: Some(
|
|
351
|
+
start_to_fire_timeout: Some(prost_dur!(from_secs(3))),
|
|
352
352
|
}
|
|
353
353
|
.into(),
|
|
354
354
|
];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
use assert_matches::assert_matches;
|
|
2
|
+
use std::{sync::Arc, time::Duration};
|
|
3
|
+
use temporal_client::{
|
|
4
|
+
ListClosedFilters, ListOpenFilters, Namespace, StartTimeFilter, WorkflowClientTrait,
|
|
5
|
+
WorkflowExecutionFilter, WorkflowOptions,
|
|
6
|
+
};
|
|
7
|
+
use temporal_sdk_core_protos::coresdk::workflow_activation::{
|
|
8
|
+
workflow_activation_job, WorkflowActivationJob,
|
|
9
|
+
};
|
|
10
|
+
use temporal_sdk_core_test_utils::{
|
|
11
|
+
get_integ_server_options, CoreWfStarter, WorkerTestHelpers, NAMESPACE,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
#[tokio::test]
|
|
15
|
+
async fn client_list_open_closed_workflow_executions() {
|
|
16
|
+
let wf_name = "client_list_open_closed_workflow_executions".to_owned();
|
|
17
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
18
|
+
let core = starter.get_worker().await;
|
|
19
|
+
let client = starter.get_client().await;
|
|
20
|
+
|
|
21
|
+
let earliest = std::time::SystemTime::now();
|
|
22
|
+
let latest = earliest + Duration::from_secs(60);
|
|
23
|
+
|
|
24
|
+
// start workflow
|
|
25
|
+
let run_id = starter
|
|
26
|
+
.start_wf_with_id(wf_name.to_owned(), WorkflowOptions::default())
|
|
27
|
+
.await;
|
|
28
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
29
|
+
assert_matches!(
|
|
30
|
+
task.jobs.as_slice(),
|
|
31
|
+
[WorkflowActivationJob {
|
|
32
|
+
variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
|
|
33
|
+
}]
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// List above OPEN workflow
|
|
37
|
+
let start_time_filter = StartTimeFilter {
|
|
38
|
+
earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
|
|
39
|
+
latest_time: Some(latest).and_then(|t| t.try_into().ok()),
|
|
40
|
+
};
|
|
41
|
+
let filter = ListOpenFilters::ExecutionFilter(WorkflowExecutionFilter {
|
|
42
|
+
workflow_id: wf_name.clone(),
|
|
43
|
+
run_id: "".to_owned(),
|
|
44
|
+
});
|
|
45
|
+
let open_workflows = client
|
|
46
|
+
.list_open_workflow_executions(1, Default::default(), Some(start_time_filter), Some(filter))
|
|
47
|
+
.await
|
|
48
|
+
.unwrap();
|
|
49
|
+
assert_eq!(open_workflows.executions.len(), 1);
|
|
50
|
+
let workflow = open_workflows.executions[0].clone();
|
|
51
|
+
assert_eq!(workflow.execution.as_ref().unwrap().workflow_id, wf_name);
|
|
52
|
+
|
|
53
|
+
// Complete workflow
|
|
54
|
+
core.complete_execution(&task.run_id).await;
|
|
55
|
+
|
|
56
|
+
// List above CLOSED workflow
|
|
57
|
+
let start_time_filter = StartTimeFilter {
|
|
58
|
+
earliest_time: Some(earliest).and_then(|t| t.try_into().ok()),
|
|
59
|
+
latest_time: Some(latest).and_then(|t| t.try_into().ok()),
|
|
60
|
+
};
|
|
61
|
+
let filter = ListClosedFilters::ExecutionFilter(WorkflowExecutionFilter {
|
|
62
|
+
workflow_id: wf_name.clone(),
|
|
63
|
+
run_id,
|
|
64
|
+
});
|
|
65
|
+
let closed_workflows = client
|
|
66
|
+
.list_closed_workflow_executions(
|
|
67
|
+
1,
|
|
68
|
+
Default::default(),
|
|
69
|
+
Some(start_time_filter),
|
|
70
|
+
Some(filter),
|
|
71
|
+
)
|
|
72
|
+
.await
|
|
73
|
+
.unwrap();
|
|
74
|
+
assert_eq!(closed_workflows.executions.len(), 1);
|
|
75
|
+
let workflow = closed_workflows.executions[0].clone();
|
|
76
|
+
assert_eq!(workflow.execution.as_ref().unwrap().workflow_id, wf_name);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#[tokio::test]
|
|
80
|
+
async fn client_describe_namespace() {
|
|
81
|
+
let client = Arc::new(
|
|
82
|
+
get_integ_server_options()
|
|
83
|
+
.connect(NAMESPACE.to_owned(), None, None)
|
|
84
|
+
.await
|
|
85
|
+
.expect("Must connect"),
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
let namespace_result = client
|
|
89
|
+
.describe_namespace(Namespace::Name(NAMESPACE.to_owned()))
|
|
90
|
+
.await
|
|
91
|
+
.unwrap();
|
|
92
|
+
assert_eq!(namespace_result.namespace_info.unwrap().name, NAMESPACE);
|
|
93
|
+
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
use assert_matches::assert_matches;
|
|
2
2
|
use std::time::Duration;
|
|
3
3
|
use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowExecutionResult, WorkflowOptions};
|
|
4
|
-
use temporal_sdk::{
|
|
4
|
+
use temporal_sdk::{
|
|
5
|
+
ActContext, ActExitValue, ActivityOptions, CancellableFuture, WfContext, WorkflowResult,
|
|
6
|
+
};
|
|
5
7
|
use temporal_sdk_core_protos::{
|
|
6
8
|
coresdk::{
|
|
7
9
|
activity_result::{
|
|
8
|
-
self, activity_resolution
|
|
9
|
-
ActivityResolution,
|
|
10
|
+
self, activity_resolution as act_res, ActivityExecutionResult, ActivityResolution,
|
|
10
11
|
},
|
|
11
12
|
activity_task::activity_task as act_task,
|
|
12
13
|
workflow_activation::{
|
|
@@ -14,13 +15,15 @@ use temporal_sdk_core_protos::{
|
|
|
14
15
|
},
|
|
15
16
|
workflow_commands::{ActivityCancellationType, RequestCancelActivity, StartTimer},
|
|
16
17
|
workflow_completion::WorkflowActivationCompletion,
|
|
17
|
-
ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt,
|
|
18
|
+
ActivityHeartbeat, ActivityTaskCompletion, AsJsonPayloadExt, FromJsonPayloadExt,
|
|
19
|
+
IntoCompletion,
|
|
18
20
|
},
|
|
19
21
|
temporal::api::{
|
|
20
22
|
common::v1::{ActivityType, Payload, Payloads},
|
|
21
23
|
enums::v1::RetryState,
|
|
22
24
|
failure::v1::{failure::FailureInfo, ActivityFailureInfo, Failure},
|
|
23
25
|
},
|
|
26
|
+
TaskToken,
|
|
24
27
|
};
|
|
25
28
|
use temporal_sdk_core_test_utils::{
|
|
26
29
|
init_core_and_create_wf, schedule_activity_cmd, CoreWfStarter, WorkerTestHelpers,
|
|
@@ -297,7 +300,7 @@ async fn activity_cancellation_try_cancel() {
|
|
|
297
300
|
),
|
|
298
301
|
StartTimer {
|
|
299
302
|
seq: 1,
|
|
300
|
-
start_to_fire_timeout: Some(
|
|
303
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
|
|
301
304
|
}
|
|
302
305
|
.into(),
|
|
303
306
|
]
|
|
@@ -359,7 +362,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
|
|
|
359
362
|
),
|
|
360
363
|
StartTimer {
|
|
361
364
|
seq: 1,
|
|
362
|
-
start_to_fire_timeout: Some(
|
|
365
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
|
|
363
366
|
}
|
|
364
367
|
.into(),
|
|
365
368
|
]
|
|
@@ -390,7 +393,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
|
|
|
390
393
|
variant: Some(workflow_activation_job::Variant::ResolveActivity(
|
|
391
394
|
ResolveActivity {
|
|
392
395
|
result: Some(ActivityResolution {
|
|
393
|
-
status: Some(
|
|
396
|
+
status: Some(act_res::Status::Cancelled(_))
|
|
394
397
|
}),
|
|
395
398
|
..
|
|
396
399
|
}
|
|
@@ -403,7 +406,7 @@ async fn activity_cancellation_plus_complete_doesnt_double_resolve() {
|
|
|
403
406
|
task.run_id,
|
|
404
407
|
vec![StartTimer {
|
|
405
408
|
seq: 2,
|
|
406
|
-
start_to_fire_timeout: Some(
|
|
409
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(100))),
|
|
407
410
|
}
|
|
408
411
|
.into()],
|
|
409
412
|
))
|
|
@@ -506,7 +509,7 @@ async fn activity_cancellation_wait_cancellation_completed() {
|
|
|
506
509
|
),
|
|
507
510
|
StartTimer {
|
|
508
511
|
seq: 1,
|
|
509
|
-
start_to_fire_timeout: Some(
|
|
512
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
|
|
510
513
|
}
|
|
511
514
|
.into(),
|
|
512
515
|
]
|
|
@@ -573,7 +576,7 @@ async fn activity_cancellation_abandon() {
|
|
|
573
576
|
),
|
|
574
577
|
StartTimer {
|
|
575
578
|
seq: 1,
|
|
576
|
-
start_to_fire_timeout: Some(
|
|
579
|
+
start_to_fire_timeout: Some(prost_dur!(from_millis(50))),
|
|
577
580
|
}
|
|
578
581
|
.into(),
|
|
579
582
|
]
|
|
@@ -793,3 +796,83 @@ async fn one_activity_abandon_cancelled_after_complete() {
|
|
|
793
796
|
.unwrap();
|
|
794
797
|
assert_matches!(res, WorkflowExecutionResult::Succeeded(_));
|
|
795
798
|
}
|
|
799
|
+
|
|
800
|
+
#[tokio::test]
|
|
801
|
+
async fn it_can_complete_async() {
|
|
802
|
+
use std::sync::Arc;
|
|
803
|
+
use tokio::sync::Mutex;
|
|
804
|
+
|
|
805
|
+
let wf_name = "it_can_complete_async".to_owned();
|
|
806
|
+
let mut starter = CoreWfStarter::new(&wf_name);
|
|
807
|
+
let mut worker = starter.worker().await;
|
|
808
|
+
let client = starter.get_client().await;
|
|
809
|
+
let async_response = "agence";
|
|
810
|
+
let shared_token: Arc<Mutex<Option<Vec<u8>>>> = Arc::new(Mutex::new(None));
|
|
811
|
+
worker.register_wf(wf_name.clone(), move |ctx: WfContext| async move {
|
|
812
|
+
let activity_resolution = ctx
|
|
813
|
+
.activity(ActivityOptions {
|
|
814
|
+
activity_type: "complete_async_activity".to_string(),
|
|
815
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
816
|
+
start_to_close_timeout: Some(Duration::from_secs(30)),
|
|
817
|
+
..Default::default()
|
|
818
|
+
})
|
|
819
|
+
.await;
|
|
820
|
+
|
|
821
|
+
let res = match activity_resolution.status {
|
|
822
|
+
Some(act_res::Status::Completed(activity_result::Success { result })) => result
|
|
823
|
+
.map(|p| String::from_json_payload(&p).unwrap())
|
|
824
|
+
.unwrap(),
|
|
825
|
+
_ => panic!("activity task failed {:?}", activity_resolution),
|
|
826
|
+
};
|
|
827
|
+
|
|
828
|
+
assert_eq!(&res, async_response);
|
|
829
|
+
Ok(().into())
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
let shared_token_ref = shared_token.clone();
|
|
833
|
+
worker.register_activity(
|
|
834
|
+
"complete_async_activity",
|
|
835
|
+
move |ctx: ActContext, _: String| {
|
|
836
|
+
let shared_token_ref = shared_token_ref.clone();
|
|
837
|
+
async move {
|
|
838
|
+
// set the `activity_task_token`
|
|
839
|
+
let activity_info = ctx.get_info();
|
|
840
|
+
let task_token = &activity_info.task_token;
|
|
841
|
+
let mut shared = shared_token_ref.lock().await;
|
|
842
|
+
*shared = Some(task_token.clone());
|
|
843
|
+
Ok::<ActExitValue<()>, _>(ActExitValue::WillCompleteAsync)
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
);
|
|
847
|
+
|
|
848
|
+
let shared_token_ref2 = shared_token.clone();
|
|
849
|
+
tokio::spawn(async move {
|
|
850
|
+
loop {
|
|
851
|
+
let mut shared = shared_token_ref2.lock().await;
|
|
852
|
+
let maybe_token = shared.take();
|
|
853
|
+
|
|
854
|
+
if let Some(task_token) = maybe_token {
|
|
855
|
+
client
|
|
856
|
+
.complete_activity_task(
|
|
857
|
+
TaskToken(task_token),
|
|
858
|
+
Some(async_response.as_json_payload().unwrap().into()),
|
|
859
|
+
)
|
|
860
|
+
.await
|
|
861
|
+
.unwrap();
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
let _run_id = worker
|
|
868
|
+
.submit_wf(
|
|
869
|
+
wf_name.to_owned(),
|
|
870
|
+
wf_name.to_owned(),
|
|
871
|
+
vec![],
|
|
872
|
+
WorkflowOptions::default(),
|
|
873
|
+
)
|
|
874
|
+
.await
|
|
875
|
+
.unwrap();
|
|
876
|
+
|
|
877
|
+
worker.run_until_done().await.unwrap();
|
|
878
|
+
}
|
|
@@ -39,7 +39,7 @@ async fn cancel_during_timer() {
|
|
|
39
39
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
|
40
40
|
// Cancel the workflow externally
|
|
41
41
|
client
|
|
42
|
-
.cancel_workflow_execution(wf_name.to_string(), None, "Dieee".to_string())
|
|
42
|
+
.cancel_workflow_execution(wf_name.to_string(), None, "Dieee".to_string(), None)
|
|
43
43
|
.await
|
|
44
44
|
.unwrap();
|
|
45
45
|
};
|
|
@@ -189,10 +189,10 @@ async fn local_act_retry_timer_backoff() {
|
|
|
189
189
|
activity_type: "echo".to_string(),
|
|
190
190
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
191
191
|
retry_policy: RetryPolicy {
|
|
192
|
-
initial_interval: Some(
|
|
192
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
193
193
|
// We want two local backoffs that are short. Third backoff will use timer
|
|
194
194
|
backoff_coefficient: 1_000.,
|
|
195
|
-
maximum_interval: Some(
|
|
195
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
196
196
|
maximum_attempts: 4,
|
|
197
197
|
non_retryable_error_types: vec![],
|
|
198
198
|
},
|
|
@@ -318,9 +318,9 @@ async fn cancel_after_act_starts(
|
|
|
318
318
|
activity_type: "echo".to_string(),
|
|
319
319
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
320
320
|
retry_policy: RetryPolicy {
|
|
321
|
-
initial_interval: Some(bo_dur.
|
|
321
|
+
initial_interval: Some(bo_dur.try_into().unwrap()),
|
|
322
322
|
backoff_coefficient: 1.,
|
|
323
|
-
maximum_interval: Some(bo_dur.
|
|
323
|
+
maximum_interval: Some(bo_dur.try_into().unwrap()),
|
|
324
324
|
// Retry forever until cancelled
|
|
325
325
|
..Default::default()
|
|
326
326
|
},
|
|
@@ -410,9 +410,9 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
|
|
|
410
410
|
activity_type: "echo".to_string(),
|
|
411
411
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
412
412
|
retry_policy: RetryPolicy {
|
|
413
|
-
initial_interval: Some(
|
|
413
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
414
414
|
backoff_coefficient: 1_000.,
|
|
415
|
-
maximum_interval: Some(
|
|
415
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
416
416
|
maximum_attempts: 4,
|
|
417
417
|
non_retryable_error_types: vec![],
|
|
418
418
|
},
|
|
@@ -467,9 +467,9 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
|
|
|
467
467
|
activity_type: "echo".to_string(),
|
|
468
468
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
469
469
|
retry_policy: RetryPolicy {
|
|
470
|
-
initial_interval: Some(
|
|
470
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
471
471
|
backoff_coefficient: 1_000.,
|
|
472
|
-
maximum_interval: Some(
|
|
472
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
473
473
|
maximum_attempts: 40,
|
|
474
474
|
non_retryable_error_types: vec![],
|
|
475
475
|
},
|
|
@@ -538,9 +538,9 @@ async fn timer_backoff_concurrent_with_non_timer_backoff() {
|
|
|
538
538
|
activity_type: "echo".to_string(),
|
|
539
539
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
540
540
|
retry_policy: RetryPolicy {
|
|
541
|
-
initial_interval: Some(
|
|
541
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
542
542
|
backoff_coefficient: 1_000.,
|
|
543
|
-
maximum_interval: Some(
|
|
543
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
544
544
|
maximum_attempts: 4,
|
|
545
545
|
non_retryable_error_types: vec![],
|
|
546
546
|
},
|
|
@@ -551,9 +551,9 @@ async fn timer_backoff_concurrent_with_non_timer_backoff() {
|
|
|
551
551
|
activity_type: "echo".to_string(),
|
|
552
552
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
553
553
|
retry_policy: RetryPolicy {
|
|
554
|
-
initial_interval: Some(
|
|
554
|
+
initial_interval: Some(prost_dur!(from_millis(15))),
|
|
555
555
|
backoff_coefficient: 10.,
|
|
556
|
-
maximum_interval: Some(
|
|
556
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
557
557
|
maximum_attempts: 4,
|
|
558
558
|
non_retryable_error_types: vec![],
|
|
559
559
|
},
|
|
@@ -593,9 +593,9 @@ async fn repro_nondeterminism_with_timer_bug() {
|
|
|
593
593
|
activity_type: "delay".to_string(),
|
|
594
594
|
input: "hi".as_json_payload().expect("serializes fine"),
|
|
595
595
|
retry_policy: RetryPolicy {
|
|
596
|
-
initial_interval: Some(
|
|
596
|
+
initial_interval: Some(prost_dur!(from_micros(15))),
|
|
597
597
|
backoff_coefficient: 1_000.,
|
|
598
|
-
maximum_interval: Some(
|
|
598
|
+
maximum_interval: Some(prost_dur!(from_millis(1500))),
|
|
599
599
|
maximum_attempts: 4,
|
|
600
600
|
non_retryable_error_types: vec![],
|
|
601
601
|
},
|
|
@@ -46,13 +46,12 @@ async fn reset_workflow() {
|
|
|
46
46
|
.await
|
|
47
47
|
.unwrap();
|
|
48
48
|
|
|
49
|
-
let client = starter.get_client().await;
|
|
49
|
+
let mut client = starter.get_client().await;
|
|
50
|
+
let client = Arc::make_mut(&mut client);
|
|
50
51
|
let resetter_fut = async {
|
|
51
52
|
notify.notified().await;
|
|
52
53
|
// Do the reset
|
|
53
54
|
client
|
|
54
|
-
.get_client()
|
|
55
|
-
.raw_retry_client()
|
|
56
55
|
.reset_workflow_execution(ResetWorkflowExecutionRequest {
|
|
57
56
|
namespace: NAMESPACE.to_owned(),
|
|
58
57
|
workflow_execution: Some(WorkflowExecution {
|
|
@@ -69,15 +68,16 @@ async fn reset_workflow() {
|
|
|
69
68
|
|
|
70
69
|
// Unblock the workflow by sending the signal. Run ID will have changed after reset so
|
|
71
70
|
// we use empty run id
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
71
|
+
WorkflowClientTrait::signal_workflow_execution(
|
|
72
|
+
client,
|
|
73
|
+
wf_name.to_owned(),
|
|
74
|
+
"".to_owned(),
|
|
75
|
+
POST_RESET_SIG.to_owned(),
|
|
76
|
+
None,
|
|
77
|
+
None,
|
|
78
|
+
)
|
|
79
|
+
.await
|
|
80
|
+
.unwrap();
|
|
81
81
|
|
|
82
82
|
// Wait for the now-reset workflow to finish
|
|
83
83
|
client
|