@temporalio/core-bridge 1.6.0 → 1.7.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.
Files changed (138) hide show
  1. package/Cargo.lock +520 -456
  2. package/lib/index.d.ts +8 -6
  3. package/lib/index.js.map +1 -1
  4. package/package.json +8 -3
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.buildkite/docker/Dockerfile +2 -2
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.buildkite/pipeline.yml +1 -1
  13. package/sdk-core/.github/workflows/heavy.yml +1 -0
  14. package/sdk-core/README.md +13 -7
  15. package/sdk-core/client/src/lib.rs +27 -9
  16. package/sdk-core/client/src/metrics.rs +17 -8
  17. package/sdk-core/client/src/raw.rs +3 -3
  18. package/sdk-core/core/Cargo.toml +3 -4
  19. package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
  20. package/sdk-core/core/src/abstractions.rs +197 -18
  21. package/sdk-core/core/src/core_tests/activity_tasks.rs +137 -45
  22. package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
  23. package/sdk-core/core/src/core_tests/determinism.rs +212 -2
  24. package/sdk-core/core/src/core_tests/local_activities.rs +183 -36
  25. package/sdk-core/core/src/core_tests/queries.rs +32 -14
  26. package/sdk-core/core/src/core_tests/workers.rs +8 -5
  27. package/sdk-core/core/src/core_tests/workflow_tasks.rs +340 -51
  28. package/sdk-core/core/src/ephemeral_server/mod.rs +110 -8
  29. package/sdk-core/core/src/internal_flags.rs +141 -0
  30. package/sdk-core/core/src/lib.rs +14 -9
  31. package/sdk-core/core/src/replay/mod.rs +16 -27
  32. package/sdk-core/core/src/telemetry/metrics.rs +69 -35
  33. package/sdk-core/core/src/telemetry/mod.rs +38 -14
  34. package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
  35. package/sdk-core/core/src/test_help/mod.rs +65 -13
  36. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
  37. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  38. package/sdk-core/core/src/worker/activities/local_activities.rs +122 -6
  39. package/sdk-core/core/src/worker/activities.rs +347 -173
  40. package/sdk-core/core/src/worker/client/mocks.rs +22 -2
  41. package/sdk-core/core/src/worker/client.rs +18 -2
  42. package/sdk-core/core/src/worker/mod.rs +137 -44
  43. package/sdk-core/core/src/worker/workflow/history_update.rs +132 -51
  44. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +207 -166
  45. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +6 -7
  46. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -7
  47. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +157 -82
  48. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +12 -12
  49. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +6 -7
  50. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +13 -15
  51. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +170 -60
  52. package/sdk-core/core/src/worker/workflow/machines/mod.rs +24 -16
  53. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +6 -8
  54. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +320 -204
  55. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +10 -13
  56. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +15 -23
  57. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +187 -46
  58. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +237 -111
  59. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +13 -13
  60. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +10 -6
  61. package/sdk-core/core/src/worker/workflow/managed_run.rs +81 -62
  62. package/sdk-core/core/src/worker/workflow/mod.rs +341 -79
  63. package/sdk-core/core/src/worker/workflow/run_cache.rs +18 -11
  64. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +15 -3
  65. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +2 -0
  66. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +75 -52
  67. package/sdk-core/core-api/Cargo.toml +0 -1
  68. package/sdk-core/core-api/src/lib.rs +13 -7
  69. package/sdk-core/core-api/src/telemetry.rs +4 -6
  70. package/sdk-core/core-api/src/worker.rs +5 -0
  71. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +80 -55
  72. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +22 -68
  73. package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
  74. package/sdk-core/histories/old_change_marker_format.bin +0 -0
  75. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
  76. package/sdk-core/protos/api_upstream/Makefile +1 -1
  77. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
  78. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
  80. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
  81. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
  82. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
  83. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
  84. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
  85. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
  86. package/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
  87. package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  88. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
  89. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
  90. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  91. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
  92. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
  93. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
  94. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  95. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  96. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
  97. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +65 -60
  98. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
  99. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
  100. package/sdk-core/sdk/Cargo.toml +1 -1
  101. package/sdk-core/sdk/src/lib.rs +21 -5
  102. package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
  103. package/sdk-core/sdk/src/workflow_context.rs +24 -17
  104. package/sdk-core/sdk/src/workflow_future.rs +9 -3
  105. package/sdk-core/sdk-core-protos/src/history_builder.rs +114 -89
  106. package/sdk-core/sdk-core-protos/src/history_info.rs +6 -1
  107. package/sdk-core/sdk-core-protos/src/lib.rs +205 -64
  108. package/sdk-core/test-utils/src/canned_histories.rs +106 -296
  109. package/sdk-core/test-utils/src/lib.rs +32 -5
  110. package/sdk-core/tests/heavy_tests.rs +10 -43
  111. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  112. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
  113. package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  114. package/sdk-core/tests/integ_tests/polling_tests.rs +3 -8
  115. package/sdk-core/tests/integ_tests/queries_tests.rs +4 -2
  116. package/sdk-core/tests/integ_tests/visibility_tests.rs +34 -23
  117. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +97 -81
  118. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  119. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -0
  120. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
  121. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +5 -1
  122. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +1 -0
  123. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +25 -3
  124. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
  125. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +30 -0
  126. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +64 -0
  127. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  128. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +4 -0
  129. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +3 -1
  130. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +7 -2
  131. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -7
  132. package/sdk-core/tests/integ_tests/workflow_tests.rs +8 -8
  133. package/sdk-core/tests/main.rs +16 -25
  134. package/sdk-core/tests/runner.rs +11 -9
  135. package/src/conversions.rs +14 -8
  136. package/src/runtime.rs +9 -8
  137. package/ts/index.ts +8 -6
  138. package/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
@@ -1,15 +1,33 @@
1
1
  use super::*;
2
2
  use futures::Future;
3
3
 
4
+ pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
5
+ signal_and_query_header: true,
6
+ internal_error_differentiation: true,
7
+ activity_failure_include_heartbeat: true,
8
+ supports_schedules: true,
9
+ encoded_failure_attributes: true,
10
+ build_id_based_versioning: true,
11
+ upsert_memo: true,
12
+ eager_workflow_start: true,
13
+ sdk_metadata: true,
14
+ };
15
+
4
16
  #[cfg(test)]
5
17
  /// Create a mock client primed with basic necessary expectations
6
18
  pub(crate) fn mock_workflow_client() -> MockWorkerClient {
7
- MockWorkerClient::new()
19
+ let mut r = MockWorkerClient::new();
20
+ r.expect_capabilities()
21
+ .returning(|| Some(DEFAULT_TEST_CAPABILITIES));
22
+ r
8
23
  }
9
24
 
10
25
  /// Create a mock manual client primed with basic necessary expectations
11
26
  pub(crate) fn mock_manual_workflow_client() -> MockManualWorkerClient {
12
- MockManualWorkerClient::new()
27
+ let mut r = MockManualWorkerClient::new();
28
+ r.expect_capabilities()
29
+ .returning(|| Some(DEFAULT_TEST_CAPABILITIES));
30
+ r
13
31
  }
14
32
 
15
33
  // Need a version of the mock that can return futures so we can return potentially pending
@@ -83,5 +101,7 @@ mockall::mock! {
83
101
  query_result: QueryResult,
84
102
  ) -> impl Future<Output = Result<RespondQueryTaskCompletedResponse>> + Send + 'b
85
103
  where 'a: 'b, Self: 'b;
104
+
105
+ fn capabilities(&self) -> Option<&'static get_system_info_response::Capabilities>;
86
106
  }
87
107
  }
@@ -7,12 +7,13 @@ use temporal_sdk_core_protos::{
7
7
  coresdk::workflow_commands::QueryResult,
8
8
  temporal::api::{
9
9
  command::v1::Command,
10
- common::v1::{Payloads, WorkflowExecution},
10
+ common::v1::{MeteringMetadata, Payloads, WorkflowExecution},
11
11
  enums::v1::{TaskQueueKind, WorkflowTaskFailedCause},
12
12
  failure::v1::Failure,
13
13
  query::v1::WorkflowQueryResult,
14
+ sdk::v1::WorkflowTaskCompletedMetadata,
14
15
  taskqueue::v1::{StickyExecutionAttributes, TaskQueue, TaskQueueMetadata, VersionId},
15
- workflowservice::v1::*,
16
+ workflowservice::v1::{get_system_info_response::Capabilities, *},
16
17
  },
17
18
  TaskToken,
18
19
  };
@@ -109,6 +110,9 @@ pub(crate) trait WorkerClient: Sync + Send {
109
110
  task_token: TaskToken,
110
111
  query_result: QueryResult,
111
112
  ) -> Result<RespondQueryTaskCompletedResponse>;
113
+
114
+ #[allow(clippy::needless_lifetimes)] // Clippy is wrong here
115
+ fn capabilities<'a>(&'a self) -> Option<&'a get_system_info_response::Capabilities>;
112
116
  }
113
117
 
114
118
  #[async_trait::async_trait]
@@ -189,6 +193,7 @@ impl WorkerClient for WorkerClientBag {
189
193
  worker_versioning_id: Some(VersionId {
190
194
  worker_build_id: self.versioning_build_id(),
191
195
  }),
196
+ messages: vec![],
192
197
  binary_checksum: self.worker_build_id.clone(),
193
198
  query_results: request
194
199
  .query_responses
@@ -206,6 +211,8 @@ impl WorkerClient for WorkerClientBag {
206
211
  })
207
212
  .collect(),
208
213
  namespace: self.namespace.clone(),
214
+ sdk_metadata: Some(request.sdk_metadata),
215
+ metering_metadata: Some(request.metering_metadata),
209
216
  };
210
217
  Ok(self
211
218
  .client
@@ -302,6 +309,7 @@ impl WorkerClient for WorkerClientBag {
302
309
  identity: self.identity.clone(),
303
310
  binary_checksum: self.worker_build_id.clone(),
304
311
  namespace: self.namespace.clone(),
312
+ messages: vec![],
305
313
  };
306
314
  Ok(self
307
315
  .client
@@ -352,6 +360,10 @@ impl WorkerClient for WorkerClientBag {
352
360
  .await?
353
361
  .into_inner())
354
362
  }
363
+
364
+ fn capabilities(&self) -> Option<&Capabilities> {
365
+ self.client.get_client().inner().capabilities()
366
+ }
355
367
  }
356
368
 
357
369
  /// A version of [RespondWorkflowTaskCompletedRequest] that will finish being filled out by the
@@ -370,4 +382,8 @@ pub(crate) struct WorkflowTaskCompletion {
370
382
  pub return_new_workflow_task: bool,
371
383
  /// Force a new WFT to be created after this completion
372
384
  pub force_create_new_workflow_task: bool,
385
+ /// SDK-specific metadata to send
386
+ pub sdk_metadata: WorkflowTaskCompletedMetadata,
387
+ /// Metering info
388
+ pub metering_metadata: MeteringMetadata,
373
389
  }
@@ -21,9 +21,12 @@ use crate::{
21
21
  WorkflowTaskPoller,
22
22
  },
23
23
  protosext::{validate_activity_completion, ValidPollWFTQResponse},
24
- telemetry::metrics::{
25
- activity_poller, local_activity_worker_type, workflow_poller, workflow_sticky_poller,
26
- MetricsContext,
24
+ telemetry::{
25
+ metrics::{
26
+ activity_poller, local_activity_worker_type, workflow_poller, workflow_sticky_poller,
27
+ MetricsContext,
28
+ },
29
+ TelemetryInstance,
27
30
  },
28
31
  worker::{
29
32
  activities::{DispatchOrTimeoutLA, LACompleteAction, LocalActivityManager},
@@ -34,7 +37,14 @@ use crate::{
34
37
  };
35
38
  use activities::{LocalInFlightActInfo, WorkerActivityTasks};
36
39
  use futures::Stream;
37
- use std::{convert::TryInto, future, sync::Arc};
40
+ use std::{
41
+ convert::TryInto,
42
+ future,
43
+ sync::{
44
+ atomic::{AtomicBool, Ordering},
45
+ Arc,
46
+ },
47
+ };
38
48
  use temporal_sdk_core_protos::{
39
49
  coresdk::{
40
50
  activity_result::activity_execution_result,
@@ -46,7 +56,7 @@ use temporal_sdk_core_protos::{
46
56
  temporal::api::{
47
57
  enums::v1::TaskQueueKind,
48
58
  taskqueue::v1::{StickyExecutionAttributes, TaskQueue},
49
- workflowservice::v1::PollActivityTaskQueueResponse,
59
+ workflowservice::v1::{get_system_info_response, PollActivityTaskQueueResponse},
50
60
  },
51
61
  TaskToken,
52
62
  };
@@ -68,7 +78,11 @@ pub struct Worker {
68
78
  shutdown_token: CancellationToken,
69
79
  /// Will be called at the end of each activation completion
70
80
  #[allow(clippy::type_complexity)] // Sorry clippy, there's no simple way to re-use here.
71
- post_activate_hook: Option<Box<dyn Fn(&Self, &str, usize) + Send + Sync>>,
81
+ post_activate_hook: Option<Box<dyn Fn(&Self, PostActivateHookData) + Send + Sync>>,
82
+ /// Set when non-local activities are complete and should stop being polled
83
+ non_local_activities_complete: Arc<AtomicBool>,
84
+ /// Set when local activities are complete and should stop being polled
85
+ local_activities_complete: Arc<AtomicBool>,
72
86
  }
73
87
 
74
88
  #[async_trait::async_trait]
@@ -135,7 +149,13 @@ impl WorkerTrait for Worker {
135
149
  self.shutdown_token.cancel();
136
150
  // First, we want to stop polling of both activity and workflow tasks
137
151
  if let Some(atm) = self.at_task_mgr.as_ref() {
138
- atm.notify_shutdown();
152
+ atm.initiate_shutdown();
153
+ }
154
+ // Let the manager know that shutdown has been initiated to try to unblock the local
155
+ // activity poll in case this worker is an activity-only worker.
156
+ self.local_act_mgr.shutdown_initiated();
157
+ if !self.workflows.ever_polled() {
158
+ self.local_act_mgr.workflows_have_shutdown();
139
159
  }
140
160
  info!(
141
161
  task_queue=%self.config.task_queue,
@@ -159,11 +179,17 @@ impl Worker {
159
179
  config: WorkerConfig,
160
180
  sticky_queue_name: Option<String>,
161
181
  client: Arc<dyn WorkerClient>,
162
- metrics: MetricsContext,
182
+ telem_instance: Option<&TelemetryInstance>,
163
183
  ) -> Self {
164
184
  info!(task_queue=%config.task_queue,
165
185
  namespace=%config.namespace,
166
186
  "Initializing worker");
187
+ let metrics = if let Some(ti) = telem_instance {
188
+ MetricsContext::top_level(config.namespace.clone(), ti)
189
+ .with_task_q(config.task_queue.clone())
190
+ } else {
191
+ MetricsContext::no_op()
192
+ };
167
193
  metrics.worker_registered();
168
194
 
169
195
  let shutdown_token = CancellationToken::new();
@@ -226,15 +252,17 @@ impl Worker {
226
252
  wft_stream,
227
253
  act_poll_buffer,
228
254
  metrics,
255
+ telem_instance,
229
256
  shutdown_token,
230
257
  )
231
258
  }
232
259
 
233
260
  #[cfg(test)]
234
261
  pub(crate) fn new_test(config: WorkerConfig, client: impl WorkerClient + 'static) -> Self {
235
- Self::new(config, None, Arc::new(client), MetricsContext::no_op())
262
+ Self::new(config, None, Arc::new(client), None)
236
263
  }
237
264
 
265
+ #[allow(clippy::too_many_arguments)] // Not much worth combining here
238
266
  pub(crate) fn new_with_pollers(
239
267
  mut config: WorkerConfig,
240
268
  sticky_queue_name: Option<String>,
@@ -242,6 +270,7 @@ impl Worker {
242
270
  wft_stream: impl Stream<Item = Result<ValidPollWFTQResponse, tonic::Status>> + Send + 'static,
243
271
  act_poller: Option<BoxedActPoller>,
244
272
  metrics: MetricsContext,
273
+ telem_instance: Option<&TelemetryInstance>,
245
274
  shutdown_token: CancellationToken,
246
275
  ) -> Self {
247
276
  let (hb_tx, hb_rx) = unbounded_channel();
@@ -251,7 +280,6 @@ impl Worker {
251
280
  hb_tx,
252
281
  metrics.with_new_attrs([local_activity_worker_type()]),
253
282
  ));
254
- let lam_clone = local_act_mgr.clone();
255
283
  let at_task_mgr = act_poller.map(|ap| {
256
284
  WorkerActivityTasks::new(
257
285
  config.max_outstanding_activities,
@@ -261,16 +289,23 @@ impl Worker {
261
289
  metrics.clone(),
262
290
  config.max_heartbeat_throttle_interval,
263
291
  config.default_heartbeat_throttle_interval,
292
+ config.graceful_shutdown_period,
264
293
  )
265
294
  });
266
- if at_task_mgr.is_none() {
295
+ let poll_on_non_local_activities = at_task_mgr.is_some();
296
+ if !poll_on_non_local_activities {
267
297
  info!("Activity polling is disabled for this worker");
268
- }
269
- let la_sink = LAReqSink::new(lam_clone, config.wf_state_inputs.clone());
298
+ };
299
+ let la_sink = LAReqSink::new(local_act_mgr.clone(), config.wf_state_inputs.clone());
270
300
  Self {
271
301
  wf_client: client.clone(),
272
302
  workflows: Workflows::new(
273
- build_wf_basics(&mut config, metrics, shutdown_token.child_token()),
303
+ build_wf_basics(
304
+ &mut config,
305
+ metrics,
306
+ shutdown_token.child_token(),
307
+ client.capabilities().cloned().unwrap_or_default(),
308
+ ),
274
309
  sticky_queue_name.map(|sq| StickyExecutionAttributes {
275
310
  worker_task_queue: Some(TaskQueue {
276
311
  name: sq,
@@ -286,16 +321,21 @@ impl Worker {
286
321
  client,
287
322
  wft_stream,
288
323
  la_sink,
324
+ local_act_mgr.clone(),
289
325
  hb_rx,
290
326
  at_task_mgr
291
327
  .as_ref()
292
328
  .map(|mgr| mgr.get_handle_for_workflows()),
329
+ telem_instance,
293
330
  ),
294
331
  at_task_mgr,
295
332
  local_act_mgr,
296
333
  config,
297
334
  shutdown_token,
298
335
  post_activate_hook: None,
336
+ // Complete if there configured not to poll on non-local activities.
337
+ non_local_activities_complete: Arc::new(AtomicBool::new(!poll_on_non_local_activities)),
338
+ local_activities_complete: Default::default(),
299
339
  }
300
340
  }
301
341
 
@@ -303,8 +343,8 @@ impl Worker {
303
343
  /// completed
304
344
  async fn shutdown(&self) {
305
345
  self.initiate_shutdown();
306
- // Next we need to wait for all local activities to finish so no more workflow task
307
- // heartbeats will be generated
346
+ // We need to wait for all local activities to finish so no more workflow task heartbeats
347
+ // will be generated
308
348
  self.local_act_mgr
309
349
  .wait_all_outstanding_tasks_finished()
310
350
  .await;
@@ -315,12 +355,13 @@ impl Worker {
315
355
  .expect("Workflow processing terminates cleanly");
316
356
  // Wait for activities to finish
317
357
  if let Some(acts) = self.at_task_mgr.as_ref() {
318
- acts.wait_all_finished().await;
358
+ acts.shutdown().await;
319
359
  }
320
360
  }
321
361
 
322
362
  /// Finish shutting down by consuming the background pollers and freeing all resources
323
363
  async fn finalize_shutdown(self) {
364
+ self.shutdown().await;
324
365
  if let Some(b) = self.at_task_mgr {
325
366
  b.shutdown().await;
326
367
  }
@@ -360,9 +401,27 @@ impl Worker {
360
401
  /// Returns `Ok(None)` in the event of a poll timeout or if the polling loop should otherwise
361
402
  /// be restarted
362
403
  async fn activity_poll(&self) -> Result<Option<ActivityTask>, PollActivityError> {
404
+ let local_activities_complete = self.local_activities_complete.load(Ordering::Relaxed);
405
+ let non_local_activities_complete =
406
+ self.non_local_activities_complete.load(Ordering::Relaxed);
407
+ if local_activities_complete && non_local_activities_complete {
408
+ return Err(PollActivityError::ShutDown);
409
+ }
363
410
  let act_mgr_poll = async {
411
+ if non_local_activities_complete {
412
+ future::pending::<()>().await;
413
+ unreachable!()
414
+ }
364
415
  if let Some(ref act_mgr) = self.at_task_mgr {
365
- act_mgr.poll().await
416
+ let res = act_mgr.poll().await;
417
+ if let Err(err) = res.as_ref() {
418
+ if matches!(err, PollActivityError::ShutDown) {
419
+ self.non_local_activities_complete
420
+ .store(true, Ordering::Relaxed);
421
+ return Ok(None);
422
+ }
423
+ };
424
+ res.map(Some)
366
425
  } else {
367
426
  // We expect the local activity branch below to produce shutdown when appropriate if
368
427
  // there are no activity pollers.
@@ -370,26 +429,35 @@ impl Worker {
370
429
  unreachable!()
371
430
  }
372
431
  };
432
+ let local_activities_poll = async {
433
+ if local_activities_complete {
434
+ future::pending::<()>().await;
435
+ unreachable!()
436
+ }
437
+ match self.local_act_mgr.next_pending().await {
438
+ Some(DispatchOrTimeoutLA::Dispatch(r)) => Ok(Some(r)),
439
+ Some(DispatchOrTimeoutLA::Timeout {
440
+ run_id,
441
+ resolution,
442
+ task,
443
+ }) => {
444
+ self.notify_local_result(&run_id, LocalResolution::LocalActivity(resolution));
445
+ Ok(task)
446
+ }
447
+ None => {
448
+ if self.shutdown_token.is_cancelled() {
449
+ self.local_activities_complete
450
+ .store(true, Ordering::Relaxed);
451
+ }
452
+ Ok(None)
453
+ }
454
+ }
455
+ };
373
456
 
374
457
  tokio::select! {
375
458
  biased;
376
459
 
377
- r = self.local_act_mgr.next_pending() => {
378
- match r {
379
- Some(DispatchOrTimeoutLA::Dispatch(r)) => Ok(Some(r)),
380
- Some(DispatchOrTimeoutLA::Timeout { run_id, resolution, task }) => {
381
- self.notify_local_result(
382
- &run_id, LocalResolution::LocalActivity(resolution));
383
- Ok(task)
384
- },
385
- None => {
386
- if self.shutdown_token.is_cancelled() {
387
- return Err(PollActivityError::ShutDown);
388
- }
389
- Ok(None)
390
- }
391
- }
392
- },
460
+ r = local_activities_poll => r,
393
461
  r = act_mgr_poll => r,
394
462
  }
395
463
  }
@@ -453,6 +521,7 @@ impl Worker {
453
521
  // about to happen anyway. Tell the local activity manager that, so that it can know to
454
522
  // cancel any remaining outstanding LAs and shutdown.
455
523
  if matches!(r, Err(PollWfError::ShutDown)) {
524
+ // This is covering the situation where WFT pollers dying is the reason for shutdown
456
525
  self.initiate_shutdown();
457
526
  self.local_act_mgr.workflows_have_shutdown();
458
527
  }
@@ -466,11 +535,15 @@ impl Worker {
466
535
  &self,
467
536
  completion: WorkflowActivationCompletion,
468
537
  ) -> Result<(), CompleteWfError> {
469
- let run_id = completion.run_id.clone();
470
- let most_recent_event = self.workflows.activation_completed(completion).await?;
471
- if let Some(h) = &self.post_activate_hook {
472
- h(self, &run_id, most_recent_event);
473
- }
538
+ self.workflows
539
+ .activation_completed(
540
+ completion,
541
+ false,
542
+ self.post_activate_hook
543
+ .as_ref()
544
+ .map(|h| |data: PostActivateHookData| h(self, data)),
545
+ )
546
+ .await?;
474
547
  Ok(())
475
548
  }
476
549
 
@@ -487,7 +560,7 @@ impl Worker {
487
560
  /// Sets a function to be called at the end of each activation completion
488
561
  pub(crate) fn set_post_activate_hook(
489
562
  &mut self,
490
- callback: impl Fn(&Self, &str, usize) + Send + Sync + 'static,
563
+ callback: impl Fn(&Self, PostActivateHookData) + Send + Sync + 'static,
491
564
  ) {
492
565
  self.post_activate_hook = Some(Box::new(callback))
493
566
  }
@@ -516,10 +589,17 @@ impl Worker {
516
589
  }
517
590
  }
518
591
 
592
+ pub struct PostActivateHookData<'a> {
593
+ pub run_id: &'a str,
594
+ pub most_recent_event: usize,
595
+ pub replaying: bool,
596
+ }
597
+
519
598
  fn build_wf_basics(
520
599
  config: &mut WorkerConfig,
521
600
  metrics: MetricsContext,
522
601
  shutdown_token: CancellationToken,
602
+ server_capabilities: get_system_info_response::Capabilities,
523
603
  ) -> WorkflowBasics {
524
604
  WorkflowBasics {
525
605
  max_cached_workflows: config.max_cached_workflows,
@@ -530,6 +610,7 @@ fn build_wf_basics(
530
610
  task_queue: config.task_queue.clone(),
531
611
  ignore_evicts_on_shutdown: config.ignore_evicts_on_shutdown,
532
612
  fetching_concurrency: config.fetching_concurrency,
613
+ server_capabilities,
533
614
  #[cfg(feature = "save_wf_inputs")]
534
615
  wf_state_inputs: config.wf_state_inputs.take(),
535
616
  }
@@ -538,11 +619,15 @@ fn build_wf_basics(
538
619
  #[cfg(test)]
539
620
  mod tests {
540
621
  use super::*;
541
- use crate::{test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client};
622
+ use crate::{
623
+ advance_fut, test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client,
624
+ };
625
+ use futures::FutureExt;
626
+
542
627
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
543
628
 
544
629
  #[tokio::test]
545
- async fn activity_timeouts_dont_eat_permits() {
630
+ async fn activity_timeouts_maintain_permit() {
546
631
  let mut mock_client = mock_workflow_client();
547
632
  mock_client
548
633
  .expect_poll_activity_task()
@@ -553,8 +638,16 @@ mod tests {
553
638
  .build()
554
639
  .unwrap();
555
640
  let worker = Worker::new_test(cfg, mock_client);
556
- assert_eq!(worker.activity_poll().await.unwrap(), None);
557
- assert_eq!(worker.at_task_mgr.unwrap().remaining_activity_capacity(), 5);
641
+ let fut = worker.poll_activity_task();
642
+ advance_fut!(fut);
643
+ assert_eq!(
644
+ worker
645
+ .at_task_mgr
646
+ .as_ref()
647
+ .unwrap()
648
+ .remaining_activity_capacity(),
649
+ 4
650
+ );
558
651
  }
559
652
 
560
653
  #[tokio::test]