@temporalio/core-bridge 1.10.2 → 1.11.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.
Files changed (126) hide show
  1. package/Cargo.lock +563 -676
  2. package/Cargo.toml +3 -3
  3. package/lib/index.d.ts +16 -5
  4. package/lib/index.js.map +1 -1
  5. package/lib/worker-tuner.d.ts +57 -0
  6. package/lib/worker-tuner.js +3 -0
  7. package/lib/worker-tuner.js.map +1 -0
  8. package/package.json +3 -3
  9. package/releases/aarch64-apple-darwin/index.node +0 -0
  10. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  11. package/releases/x86_64-apple-darwin/index.node +0 -0
  12. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  13. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  14. package/sdk-core/.github/workflows/heavy.yml +6 -11
  15. package/sdk-core/.github/workflows/per-pr.yml +23 -41
  16. package/sdk-core/Cargo.toml +5 -5
  17. package/sdk-core/README.md +2 -0
  18. package/sdk-core/client/Cargo.toml +4 -2
  19. package/sdk-core/client/src/lib.rs +60 -17
  20. package/sdk-core/client/src/metrics.rs +1 -1
  21. package/sdk-core/client/src/proxy.rs +17 -12
  22. package/sdk-core/client/src/raw.rs +218 -69
  23. package/sdk-core/client/src/retry.rs +19 -9
  24. package/sdk-core/core/Cargo.toml +12 -12
  25. package/sdk-core/core/src/abstractions.rs +3 -3
  26. package/sdk-core/core/src/core_tests/activity_tasks.rs +2 -1
  27. package/sdk-core/core/src/core_tests/determinism.rs +1 -1
  28. package/sdk-core/core/src/core_tests/local_activities.rs +73 -10
  29. package/sdk-core/core/src/core_tests/queries.rs +2 -1
  30. package/sdk-core/core/src/core_tests/updates.rs +162 -4
  31. package/sdk-core/core/src/core_tests/workers.rs +38 -2
  32. package/sdk-core/core/src/core_tests/workflow_tasks.rs +158 -27
  33. package/sdk-core/core/src/internal_flags.rs +17 -7
  34. package/sdk-core/core/src/lib.rs +9 -3
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +1 -10
  36. package/sdk-core/core/src/protosext/mod.rs +0 -1
  37. package/sdk-core/core/src/protosext/protocol_messages.rs +105 -16
  38. package/sdk-core/core/src/retry_logic.rs +22 -2
  39. package/sdk-core/core/src/telemetry/otel.rs +44 -12
  40. package/sdk-core/core/src/test_help/mod.rs +65 -12
  41. package/sdk-core/core/src/worker/activities/local_activities.rs +1 -4
  42. package/sdk-core/core/src/worker/activities.rs +3 -4
  43. package/sdk-core/core/src/worker/client/mocks.rs +7 -6
  44. package/sdk-core/core/src/worker/client.rs +11 -2
  45. package/sdk-core/core/src/worker/mod.rs +49 -24
  46. package/sdk-core/core/src/worker/tuner/resource_based.rs +48 -48
  47. package/sdk-core/core/src/worker/tuner.rs +124 -4
  48. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +1 -1
  49. package/sdk-core/core/src/worker/workflow/history_update.rs +11 -2
  50. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +18 -3
  51. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -0
  52. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -0
  53. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -0
  54. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -0
  55. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +1 -0
  56. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -0
  57. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +4 -4
  58. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +1 -0
  59. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -0
  60. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -0
  61. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +3 -1
  62. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +38 -28
  63. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +4 -2
  64. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +95 -71
  65. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +1 -1
  66. package/sdk-core/core/src/worker/workflow/managed_run.rs +214 -14
  67. package/sdk-core/core/src/worker/workflow/mod.rs +49 -36
  68. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +1 -2
  69. package/sdk-core/core-api/src/errors.rs +13 -7
  70. package/sdk-core/core-api/src/lib.rs +9 -1
  71. package/sdk-core/sdk/Cargo.toml +1 -1
  72. package/sdk-core/sdk/src/activity_context.rs +3 -4
  73. package/sdk-core/sdk/src/lib.rs +96 -49
  74. package/sdk-core/sdk/src/workflow_context/options.rs +8 -4
  75. package/sdk-core/sdk/src/workflow_context.rs +53 -49
  76. package/sdk-core/sdk/src/workflow_future.rs +10 -4
  77. package/sdk-core/sdk-core-protos/Cargo.toml +4 -3
  78. package/sdk-core/sdk-core-protos/build.rs +2 -0
  79. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/.github/workflows/build.yaml +18 -0
  80. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/LICENSE +21 -0
  81. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/Makefile +59 -0
  82. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/README.md +25 -0
  83. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -0
  84. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.gen.yaml +14 -0
  85. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.lock +8 -0
  86. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.yaml +9 -0
  87. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +520 -0
  88. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +263 -0
  89. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +173 -0
  90. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +164 -0
  91. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +36 -0
  92. package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/region/v1/message.proto +22 -0
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +50 -8
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/.gitmodules +3 -0
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +132 -54
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +177 -81
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +13 -0
  98. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  99. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +8 -3
  100. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +10 -0
  101. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +3 -3
  102. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +6 -6
  103. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/enhanced_stack_trace.proto +96 -0
  104. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/user_metadata.proto +49 -0
  105. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +6 -7
  106. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +55 -24
  107. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +7 -0
  108. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +21 -4
  109. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +64 -45
  110. package/sdk-core/sdk-core-protos/src/history_builder.rs +8 -1
  111. package/sdk-core/sdk-core-protos/src/lib.rs +40 -10
  112. package/sdk-core/test-utils/src/canned_histories.rs +1 -1
  113. package/sdk-core/tests/fuzzy_workflow.rs +4 -2
  114. package/sdk-core/tests/heavy_tests.rs +3 -3
  115. package/sdk-core/tests/integ_tests/activity_functions.rs +2 -2
  116. package/sdk-core/tests/integ_tests/client_tests.rs +234 -6
  117. package/sdk-core/tests/integ_tests/update_tests.rs +180 -47
  118. package/sdk-core/tests/integ_tests/worker_tests.rs +32 -0
  119. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +47 -3
  120. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +10 -10
  121. package/sdk-core/tests/main.rs +1 -0
  122. package/src/conversions.rs +84 -11
  123. package/src/runtime.rs +5 -17
  124. package/src/worker.rs +27 -6
  125. package/ts/index.ts +24 -5
  126. package/ts/worker-tuner.ts +71 -0
@@ -1,7 +1,25 @@
1
- use std::time::Duration;
2
- use temporal_client::{RetryClient, WorkflowClientTrait, WorkflowService};
3
- use temporal_sdk_core_protos::temporal::api::workflowservice::v1::DescribeNamespaceRequest;
1
+ use assert_matches::assert_matches;
2
+ use futures_util::{future::BoxFuture, FutureExt};
3
+ use std::{
4
+ collections::HashMap,
5
+ convert::Infallible,
6
+ env,
7
+ task::{Context, Poll},
8
+ time::Duration,
9
+ };
10
+ use temporal_client::{Namespace, RetryConfig, WorkflowClientTrait, WorkflowService};
11
+ use temporal_sdk_core_protos::temporal::api::{
12
+ cloud::cloudservice::v1::GetNamespaceRequest, workflowservice::v1::DescribeNamespaceRequest,
13
+ };
4
14
  use temporal_sdk_core_test_utils::{get_integ_server_options, CoreWfStarter, NAMESPACE};
15
+ use tokio::{
16
+ net::TcpListener,
17
+ sync::{mpsc::UnboundedSender, oneshot},
18
+ };
19
+ use tonic::{
20
+ body::BoxBody, codegen::Service, server::NamedService, transport::Server, Code, Request,
21
+ };
22
+ use tracing::info;
5
23
 
6
24
  #[tokio::test]
7
25
  async fn can_use_retry_client() {
@@ -17,9 +35,8 @@ async fn can_use_retry_client() {
17
35
  #[tokio::test]
18
36
  async fn can_use_retry_raw_client() {
19
37
  let opts = get_integ_server_options();
20
- let raw_client = opts.connect_no_namespace(None).await.unwrap();
21
- let mut retry_client = RetryClient::new(raw_client, opts.retry_config);
22
- retry_client
38
+ let mut client = opts.connect_no_namespace(None).await.unwrap();
39
+ client
23
40
  .describe_namespace(DescribeNamespaceRequest {
24
41
  namespace: NAMESPACE.to_string(),
25
42
  ..Default::default()
@@ -34,3 +51,214 @@ async fn calls_get_system_info() {
34
51
  let raw_client = opts.connect_no_namespace(None).await.unwrap();
35
52
  assert!(raw_client.get_client().capabilities().is_some());
36
53
  }
54
+
55
+ #[tokio::test]
56
+ async fn per_call_timeout_respected_whole_client() {
57
+ let opts = get_integ_server_options();
58
+ let mut raw_client = opts.connect_no_namespace(None).await.unwrap();
59
+ let mut hm = HashMap::new();
60
+ hm.insert("grpc-timeout".to_string(), "0S".to_string());
61
+ raw_client.get_client().set_headers(hm);
62
+ let err = raw_client
63
+ .describe_namespace(DescribeNamespaceRequest {
64
+ namespace: NAMESPACE.to_string(),
65
+ ..Default::default()
66
+ })
67
+ .await
68
+ .unwrap_err();
69
+ assert_matches!(err.code(), Code::DeadlineExceeded | Code::Cancelled);
70
+ }
71
+
72
+ #[tokio::test]
73
+ async fn per_call_timeout_respected_one_call() {
74
+ let opts = get_integ_server_options();
75
+ let mut client = opts.connect_no_namespace(None).await.unwrap();
76
+
77
+ let mut req = Request::new(DescribeNamespaceRequest {
78
+ namespace: NAMESPACE.to_string(),
79
+ ..Default::default()
80
+ });
81
+ req.set_timeout(Duration::from_millis(0));
82
+ let res = client.describe_namespace(req).await;
83
+ assert_matches!(
84
+ res.unwrap_err().code(),
85
+ Code::DeadlineExceeded | Code::Cancelled
86
+ );
87
+ }
88
+
89
+ #[derive(Clone)]
90
+ struct GenericService {
91
+ header_to_parse: &'static str,
92
+ header_tx: UnboundedSender<String>,
93
+ }
94
+ impl Service<tonic::codegen::http::Request<BoxBody>> for GenericService {
95
+ type Response = tonic::codegen::http::Response<BoxBody>;
96
+ type Error = Infallible;
97
+ type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
98
+
99
+ fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
100
+ Poll::Ready(Ok(()))
101
+ }
102
+
103
+ fn call(&mut self, req: tonic::codegen::http::Request<BoxBody>) -> Self::Future {
104
+ self.header_tx
105
+ .send(
106
+ String::from_utf8_lossy(
107
+ req.headers()
108
+ .get(self.header_to_parse)
109
+ .map(|hv| hv.as_bytes())
110
+ .unwrap_or_default(),
111
+ )
112
+ .to_string(),
113
+ )
114
+ .unwrap();
115
+ async move { Ok(Self::Response::new(tonic::codegen::empty_body())) }.boxed()
116
+ }
117
+ }
118
+ impl NamedService for GenericService {
119
+ const NAME: &'static str = "temporal.api.workflowservice.v1.WorkflowService";
120
+ }
121
+
122
+ #[tokio::test]
123
+ async fn timeouts_respected_one_call_fake_server() {
124
+ let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();
125
+ let (header_tx, mut header_rx) = tokio::sync::mpsc::unbounded_channel();
126
+
127
+ let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
128
+ let addr = listener.local_addr().unwrap();
129
+
130
+ let server_handle = tokio::spawn(async move {
131
+ Server::builder()
132
+ .add_service(GenericService {
133
+ header_to_parse: "grpc-timeout",
134
+ header_tx,
135
+ })
136
+ .serve_with_incoming_shutdown(
137
+ tokio_stream::wrappers::TcpListenerStream::new(listener),
138
+ async {
139
+ shutdown_rx.await.ok();
140
+ },
141
+ )
142
+ .await
143
+ .unwrap();
144
+ });
145
+
146
+ let mut opts = get_integ_server_options();
147
+ let uri = format!("http://localhost:{}", addr.port()).parse().unwrap();
148
+ opts.target_url = uri;
149
+ opts.skip_get_system_info = true;
150
+ opts.retry_config = RetryConfig {
151
+ max_retries: 1,
152
+ ..Default::default()
153
+ };
154
+
155
+ let mut client = opts.connect_no_namespace(None).await.unwrap();
156
+
157
+ macro_rules! call_client {
158
+ ($client:ident, $trx:ident, $client_fn:ident) => {
159
+ let mut req = Request::new(Default::default());
160
+ req.set_timeout(Duration::from_millis(100));
161
+ // Response is always error b/c empty body
162
+ let _ = $client.$client_fn(req).await;
163
+ let timeout = $trx.recv().await.unwrap();
164
+ assert_eq!("100000u", timeout);
165
+ };
166
+ }
167
+
168
+ call_client!(client, header_rx, get_workflow_execution_history);
169
+ call_client!(client, header_rx, update_workflow_execution);
170
+ call_client!(client, header_rx, poll_workflow_execution_update);
171
+
172
+ // Shutdown the server
173
+ shutdown_tx.send(()).unwrap();
174
+ server_handle.await.unwrap();
175
+ }
176
+
177
+ #[tokio::test]
178
+ async fn namespace_header_attached_to_relevant_calls() {
179
+ let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>();
180
+ let (header_tx, mut header_rx) = tokio::sync::mpsc::unbounded_channel();
181
+
182
+ let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
183
+ let addr = listener.local_addr().unwrap();
184
+
185
+ let server_handle = tokio::spawn(async move {
186
+ Server::builder()
187
+ .add_service(GenericService {
188
+ header_to_parse: "Temporal-Namespace",
189
+ header_tx,
190
+ })
191
+ .serve_with_incoming_shutdown(
192
+ tokio_stream::wrappers::TcpListenerStream::new(listener),
193
+ async {
194
+ shutdown_rx.await.ok();
195
+ },
196
+ )
197
+ .await
198
+ .unwrap();
199
+ });
200
+
201
+ let mut opts = get_integ_server_options();
202
+ let uri = format!("http://localhost:{}", addr.port()).parse().unwrap();
203
+ opts.target_url = uri;
204
+ opts.skip_get_system_info = true;
205
+ opts.retry_config = RetryConfig {
206
+ max_retries: 1,
207
+ ..Default::default()
208
+ };
209
+
210
+ let namespace = "namespace";
211
+ let client = opts.connect(namespace, None).await.unwrap();
212
+
213
+ let _ = client
214
+ .get_workflow_execution_history("hi".to_string(), None, vec![])
215
+ .await;
216
+ let val = header_rx.recv().await.unwrap();
217
+ assert_eq!(namespace, val);
218
+ let _ = client.list_namespaces().await;
219
+ let val = header_rx.recv().await.unwrap();
220
+ // List namespaces is not namespace-specific
221
+ assert_eq!("", val);
222
+ let _ = client
223
+ .describe_namespace(Namespace::Name("Other".to_string()))
224
+ .await;
225
+ let val = header_rx.recv().await.unwrap();
226
+ assert_eq!("Other", val);
227
+
228
+ // Shutdown the server
229
+ shutdown_tx.send(()).unwrap();
230
+ server_handle.await.unwrap();
231
+ }
232
+
233
+ #[tokio::test]
234
+ async fn cloud_ops_test() {
235
+ let api_key = match env::var("TEMPORAL_CLIENT_CLOUD_API_KEY") {
236
+ Ok(k) => k,
237
+ Err(_) => {
238
+ // skip test
239
+ info!("Skipped cloud operations client test");
240
+ return;
241
+ }
242
+ };
243
+ let api_version =
244
+ env::var("TEMPORAL_CLIENT_CLOUD_API_VERSION").expect("version env var must exist");
245
+ let namespace =
246
+ env::var("TEMPORAL_CLIENT_CLOUD_NAMESPACE").expect("namespace env var must exist");
247
+ let mut opts = get_integ_server_options();
248
+ opts.target_url = "saas-api.tmprl.cloud:443".parse().unwrap();
249
+ opts.api_key = Some(api_key);
250
+ opts.headers = Some({
251
+ let mut hm = HashMap::new();
252
+ hm.insert("temporal-cloud-api-version".to_string(), api_version);
253
+ hm
254
+ });
255
+ let mut client = opts.connect_no_namespace(None).await.unwrap().into_inner();
256
+ let cloud_client = client.cloud_svc_mut();
257
+ let res = cloud_client
258
+ .get_namespace(GetNamespaceRequest {
259
+ namespace: namespace.clone(),
260
+ })
261
+ .await
262
+ .unwrap();
263
+ assert_eq!(res.into_inner().namespace.unwrap().namespace, namespace);
264
+ }
@@ -1,12 +1,15 @@
1
- use anyhow::{anyhow, bail};
1
+ use anyhow::anyhow;
2
2
  use assert_matches::assert_matches;
3
3
  use futures_util::{future, future::join_all, StreamExt};
4
4
  use once_cell::sync::Lazy;
5
5
  use std::{
6
- sync::atomic::{AtomicBool, AtomicUsize, Ordering},
6
+ sync::{
7
+ atomic::{AtomicBool, AtomicUsize, Ordering},
8
+ Arc,
9
+ },
7
10
  time::Duration,
8
11
  };
9
- use temporal_client::WorkflowClientTrait;
12
+ use temporal_client::{Client, RetryClient, WorkflowClientTrait, WorkflowService};
10
13
  use temporal_sdk::{ActContext, ActivityOptions, LocalActivityOptions, UpdateContext, WfContext};
11
14
  use temporal_sdk_core::replay::HistoryForReplay;
12
15
  use temporal_sdk_core_api::Worker;
@@ -23,29 +26,159 @@ use temporal_sdk_core_protos::{
23
26
  ActivityTaskCompletion, AsJsonPayloadExt, IntoPayloadsExt,
24
27
  },
25
28
  temporal::api::{
26
- enums::v1::{EventType, UpdateWorkflowExecutionLifecycleStage},
27
- update,
28
- update::v1::WaitPolicy,
29
+ common::v1::WorkflowExecution,
30
+ enums::v1::{EventType, ResetReapplyType, UpdateWorkflowExecutionLifecycleStage},
31
+ update::{self, v1::WaitPolicy},
32
+ workflowservice::v1::ResetWorkflowExecutionRequest,
29
33
  },
30
34
  };
31
35
  use temporal_sdk_core_test_utils::{
32
- init_core_and_create_wf, init_core_replay_preloaded, start_timer_cmd, CoreWfStarter,
33
- WorkerTestHelpers,
36
+ drain_pollers_and_shutdown, init_core_and_create_wf, init_core_replay_preloaded,
37
+ start_timer_cmd, CoreWfStarter, WorkerTestHelpers,
34
38
  };
35
39
  use tokio::{join, sync::Barrier};
40
+ use uuid::Uuid;
41
+
42
+ #[derive(Clone, Copy)]
43
+ enum FailUpdate {
44
+ Yes,
45
+ No,
46
+ }
47
+
48
+ #[derive(Clone, Copy)]
49
+ enum CompleteWorkflow {
50
+ Yes,
51
+ No,
52
+ }
36
53
 
37
54
  #[rstest::rstest]
38
55
  #[tokio::test]
39
- async fn update_workflow(#[values(true, false)] will_fail: bool) {
56
+ async fn update_workflow(#[values(FailUpdate::Yes, FailUpdate::No)] will_fail: FailUpdate) {
40
57
  let mut starter = init_core_and_create_wf("update_workflow").await;
41
58
  let core = starter.get_worker().await;
42
59
  let client = starter.get_client().await;
43
- let workflow_id = starter.get_task_queue().to_string();
44
-
60
+ let workflow_id = starter.get_task_queue();
45
61
  let update_id = "some_update";
46
- // Task is completed with no commands
47
- let res = core.poll_workflow_activation().await.unwrap();
48
- core.complete_workflow_activation(WorkflowActivationCompletion::empty(res.run_id.clone()))
62
+ send_and_handle_update(
63
+ workflow_id,
64
+ update_id,
65
+ will_fail,
66
+ CompleteWorkflow::Yes,
67
+ core.as_ref(),
68
+ client.as_ref(),
69
+ )
70
+ .await;
71
+
72
+ // Make sure replay works
73
+ let history = client
74
+ .get_workflow_execution_history(workflow_id.to_string(), None, vec![])
75
+ .await
76
+ .unwrap()
77
+ .history
78
+ .unwrap();
79
+ let with_id = HistoryForReplay::new(history, workflow_id.to_string());
80
+ let replay_worker = init_core_replay_preloaded(workflow_id, [with_id]);
81
+ handle_update(will_fail, CompleteWorkflow::Yes, replay_worker.as_ref()).await;
82
+ }
83
+
84
+ #[rstest::rstest]
85
+ #[tokio::test]
86
+ async fn reapplied_updates_due_to_reset() {
87
+ let mut starter = init_core_and_create_wf("update_workflow").await;
88
+ let core = starter.get_worker().await;
89
+ let client = starter.get_client().await;
90
+ let workflow_id = starter.get_task_queue();
91
+ let pre_reset_run_id = send_and_handle_update(
92
+ workflow_id,
93
+ "first-update",
94
+ FailUpdate::No,
95
+ CompleteWorkflow::Yes,
96
+ core.as_ref(),
97
+ client.as_ref(),
98
+ )
99
+ .await;
100
+
101
+ // Reset to before the update was accepted
102
+ let workflow_task_finish_event_id = 4;
103
+
104
+ let mut client_mut = client.clone();
105
+ let reset_response = WorkflowService::reset_workflow_execution(
106
+ Arc::make_mut(&mut client_mut),
107
+ ResetWorkflowExecutionRequest {
108
+ namespace: client.namespace().into(),
109
+ workflow_execution: Some(WorkflowExecution {
110
+ workflow_id: workflow_id.into(),
111
+ run_id: pre_reset_run_id.clone(),
112
+ }),
113
+ workflow_task_finish_event_id,
114
+ reset_reapply_type: ResetReapplyType::AllEligible as i32,
115
+ request_id: Uuid::new_v4().to_string(),
116
+ ..Default::default()
117
+ },
118
+ )
119
+ .await
120
+ .unwrap()
121
+ .into_inner();
122
+
123
+ // Accept and complete the reapplied update
124
+ handle_update(FailUpdate::No, CompleteWorkflow::No, core.as_ref()).await;
125
+
126
+ // Send a second update and complete the workflow
127
+ let post_reset_run_id = send_and_handle_update(
128
+ workflow_id,
129
+ "second-update",
130
+ FailUpdate::No,
131
+ CompleteWorkflow::Yes,
132
+ core.as_ref(),
133
+ client.as_ref(),
134
+ )
135
+ .await;
136
+
137
+ assert_eq!(post_reset_run_id, reset_response.run_id);
138
+
139
+ // Make sure replay works
140
+ let history = client
141
+ .get_workflow_execution_history(workflow_id.to_string(), Some(post_reset_run_id), vec![])
142
+ .await
143
+ .unwrap()
144
+ .history
145
+ .unwrap();
146
+ let with_id = HistoryForReplay::new(history, workflow_id.to_string());
147
+
148
+ let replay_worker = init_core_replay_preloaded(workflow_id, [with_id]);
149
+ // We now recapitulate the actions that the worker took on first execution above, pretending
150
+ // that we always followed the post-reset history.
151
+ // First, we handled the post-reset reapplied update and did not complete the workflow.
152
+ handle_update(FailUpdate::No, CompleteWorkflow::No, replay_worker.as_ref()).await;
153
+ // Then the client sent a second update; we handled it and completed the workflow.
154
+ handle_update(
155
+ FailUpdate::No,
156
+ CompleteWorkflow::Yes,
157
+ replay_worker.as_ref(),
158
+ )
159
+ .await;
160
+
161
+ // This is a replay worker and there is a remaining activation containing a RemoveFromCache job.
162
+ let act = replay_worker.poll_workflow_activation().await.unwrap();
163
+ replay_worker
164
+ .complete_workflow_activation(WorkflowActivationCompletion::empty(act.run_id))
165
+ .await
166
+ .unwrap();
167
+ drain_pollers_and_shutdown(&replay_worker).await;
168
+ }
169
+
170
+ // Start a workflow, send an update, accept the update, complete the update, complete the workflow.
171
+ async fn send_and_handle_update(
172
+ workflow_id: &str,
173
+ update_id: &str,
174
+ fail_update: FailUpdate,
175
+ complete_workflow: CompleteWorkflow,
176
+ core: &dyn Worker,
177
+ client: &RetryClient<Client>,
178
+ ) -> String {
179
+ // Complete first task with no commands
180
+ let act = core.poll_workflow_activation().await.unwrap();
181
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(act.run_id.clone()))
49
182
  .await
50
183
  .unwrap();
51
184
 
@@ -54,7 +187,7 @@ async fn update_workflow(#[values(true, false)] will_fail: bool) {
54
187
  client
55
188
  .update_workflow_execution(
56
189
  workflow_id.to_string(),
57
- res.run_id.to_string(),
190
+ act.run_id.to_string(),
58
191
  update_id.to_string(),
59
192
  WaitPolicy {
60
193
  lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
@@ -65,40 +198,38 @@ async fn update_workflow(#[values(true, false)] will_fail: bool) {
65
198
  .unwrap()
66
199
  };
67
200
 
68
- let processing_task = _do_update_workflow(will_fail, core.as_ref());
201
+ // Accept update, complete update and complete workflow
202
+ let processing_task = handle_update(fail_update, complete_workflow, core);
69
203
  let (ur, _) = join!(update_task, processing_task);
70
204
 
71
205
  let v = ur.outcome.unwrap().value.unwrap();
72
- if will_fail {
73
- assert_matches!(v, update::v1::outcome::Value::Failure(_));
74
- } else {
75
- assert_matches!(v, update::v1::outcome::Value::Success(_));
206
+ match fail_update {
207
+ FailUpdate::Yes => assert_matches!(v, update::v1::outcome::Value::Failure(_)),
208
+ FailUpdate::No => assert_matches!(v, update::v1::outcome::Value::Success(_)),
76
209
  }
77
-
78
- // Make sure replay works
79
- let history = client
80
- .get_workflow_execution_history(workflow_id.clone(), None, vec![])
81
- .await
82
- .unwrap()
83
- .history
84
- .unwrap();
85
- let with_id = HistoryForReplay::new(history, workflow_id.clone());
86
- let replay_worker = init_core_replay_preloaded(&workflow_id, [with_id]);
87
- _do_update_workflow(will_fail, replay_worker.as_ref()).await;
210
+ act.run_id
88
211
  }
89
212
 
90
- async fn _do_update_workflow(will_fail: bool, core: &dyn Worker) {
91
- let res = core.poll_workflow_activation().await.unwrap();
213
+ // Accept and then complete update. If `FailUpdate::Yes` then complete the update as a failure; if
214
+ // `CompleteWorkflow::Yes` then additionally complete the workflow. Timers are created when further
215
+ // activations are required (i.e., on accepting-but-not-completing the update, and on completing the
216
+ // update if not also completing the workflow).
217
+ async fn handle_update(
218
+ fail_update: FailUpdate,
219
+ complete_workflow: CompleteWorkflow,
220
+ core: &dyn Worker,
221
+ ) {
222
+ let act = core.poll_workflow_activation().await.unwrap();
92
223
  // On replay, the first activation has update & start workflow, but on first execution, it does
93
224
  // not - can happen if update is waiting on some condition.
94
225
  let pid = assert_matches!(
95
- &res.jobs[0],
226
+ &act.jobs[0],
96
227
  WorkflowActivationJob {
97
228
  variant: Some(workflow_activation_job::Variant::DoUpdate(d)),
98
229
  } => &d.protocol_instance_id
99
230
  );
100
231
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
101
- res.run_id,
232
+ act.run_id,
102
233
  vec![
103
234
  UpdateResponse {
104
235
  protocol_instance_id: pid.to_string(),
@@ -111,26 +242,28 @@ async fn _do_update_workflow(will_fail: bool, core: &dyn Worker) {
111
242
  .await
112
243
  .unwrap();
113
244
 
114
- let response = if will_fail {
115
- UpdateResponse {
245
+ // Timer fires
246
+ let act = core.poll_workflow_activation().await.unwrap();
247
+ let update_response = match fail_update {
248
+ FailUpdate::Yes => UpdateResponse {
116
249
  protocol_instance_id: pid.to_string(),
117
250
  response: Some(update_response::Response::Rejected(
118
251
  "uh oh spaghettios!".into(),
119
252
  )),
120
- }
121
- } else {
122
- UpdateResponse {
253
+ },
254
+ FailUpdate::No => UpdateResponse {
123
255
  protocol_instance_id: pid.to_string(),
124
256
  response: Some(update_response::Response::Completed("done!".into())),
125
- }
257
+ },
126
258
  };
127
- let res = core.poll_workflow_activation().await.unwrap();
128
- // Timer fires
129
259
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
130
- res.run_id,
260
+ act.run_id,
131
261
  vec![
132
- response.into(),
133
- CompleteWorkflowExecution { result: None }.into(),
262
+ update_response.into(),
263
+ match complete_workflow {
264
+ CompleteWorkflow::Yes => CompleteWorkflowExecution { result: None }.into(),
265
+ CompleteWorkflow::No => start_timer_cmd(1, Duration::from_millis(1)),
266
+ },
134
267
  ],
135
268
  ))
136
269
  .await
@@ -843,7 +976,7 @@ async fn worker_restarted_in_middle_of_update() {
843
976
  BARR.wait().await;
844
977
  if !ACT_RAN.fetch_or(true, Ordering::Relaxed) {
845
978
  // On first run fail the task so we'll get retried on the new worker
846
- bail!("Fail first time");
979
+ return Err(anyhow!("Fail first time").into());
847
980
  }
848
981
  Ok(echo_me)
849
982
  });
@@ -0,0 +1,32 @@
1
+ use assert_matches::assert_matches;
2
+ use temporal_sdk_core::{init_worker, CoreRuntime};
3
+ use temporal_sdk_core_api::{errors::WorkerValidationError, worker::WorkerConfigBuilder, Worker};
4
+ use temporal_sdk_core_test_utils::{get_integ_server_options, get_integ_telem_options};
5
+
6
+ #[tokio::test]
7
+ async fn worker_validation_fails_on_nonexistent_namespace() {
8
+ let opts = get_integ_server_options();
9
+ let runtime = CoreRuntime::new_assume_tokio(get_integ_telem_options()).unwrap();
10
+ let retrying_client = opts
11
+ .connect_no_namespace(runtime.telemetry().get_temporal_metric_meter())
12
+ .await
13
+ .unwrap();
14
+
15
+ let worker = init_worker(
16
+ &runtime,
17
+ WorkerConfigBuilder::default()
18
+ .namespace("i_dont_exist")
19
+ .task_queue("Wheee!")
20
+ .worker_build_id("blah")
21
+ .build()
22
+ .unwrap(),
23
+ retrying_client,
24
+ )
25
+ .unwrap();
26
+
27
+ let res = worker.validate().await;
28
+ assert_matches!(
29
+ res,
30
+ Err(WorkerValidationError::NamespaceDescribeError { .. })
31
+ );
32
+ }
@@ -8,8 +8,8 @@ use std::{
8
8
  };
9
9
  use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowExecutionResult, WorkflowOptions};
10
10
  use temporal_sdk::{
11
- ActContext, ActExitValue, ActivityCancelledError, ActivityOptions, CancellableFuture,
12
- WfContext, WorkflowResult,
11
+ ActContext, ActExitValue, ActivityError, ActivityOptions, CancellableFuture, WfContext,
12
+ WorkflowResult,
13
13
  };
14
14
  use temporal_sdk_core_protos::{
15
15
  coresdk::{
@@ -787,6 +787,50 @@ async fn activity_cancelled_after_heartbeat_times_out() {
787
787
  .unwrap();
788
788
  }
789
789
 
790
+ #[tokio::test]
791
+ async fn one_activity_abandon_cancelled_before_started() {
792
+ let wf_name = "one_activity_abandon_cancelled_before_started";
793
+ let mut starter = CoreWfStarter::new(wf_name);
794
+ let mut worker = starter.worker().await;
795
+ let client = starter.get_client().await;
796
+ worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
797
+ let act_fut = ctx.activity(ActivityOptions {
798
+ activity_type: "echo_activity".to_string(),
799
+ start_to_close_timeout: Some(Duration::from_secs(5)),
800
+ input: "hi!".as_json_payload().expect("serializes fine"),
801
+ cancellation_type: ActivityCancellationType::Abandon,
802
+ ..Default::default()
803
+ });
804
+ act_fut.cancel(&ctx);
805
+ act_fut.await;
806
+ Ok(().into())
807
+ });
808
+ worker.register_activity(
809
+ "echo_activity",
810
+ |_ctx: ActContext, echo_me: String| async move {
811
+ sleep(Duration::from_secs(2)).await;
812
+ Ok(echo_me)
813
+ },
814
+ );
815
+
816
+ let run_id = worker
817
+ .submit_wf(
818
+ wf_name.to_owned(),
819
+ wf_name.to_owned(),
820
+ vec![],
821
+ WorkflowOptions::default(),
822
+ )
823
+ .await
824
+ .unwrap();
825
+ worker.run_until_done().await.unwrap();
826
+ let handle = client.get_untyped_workflow_handle(wf_name, run_id);
827
+ let res = handle
828
+ .get_workflow_result(Default::default())
829
+ .await
830
+ .unwrap();
831
+ assert_matches!(res, WorkflowExecutionResult::Succeeded(_));
832
+ }
833
+
790
834
  #[tokio::test]
791
835
  async fn one_activity_abandon_cancelled_after_complete() {
792
836
  let wf_name = "one_activity_abandon_cancelled_after_complete";
@@ -946,7 +990,7 @@ async fn graceful_shutdown() {
946
990
  // just wait to be cancelled
947
991
  ctx.cancelled().await;
948
992
  ACTS_DONE.add_permits(1);
949
- Result::<(), _>::Err(ActivityCancelledError::default().into())
993
+ Result::<(), _>::Err(ActivityError::cancelled())
950
994
  });
951
995
 
952
996
  worker