@temporalio/core-bridge 1.11.2 → 1.11.4

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 (119) hide show
  1. package/Cargo.lock +396 -489
  2. package/Cargo.toml +3 -2
  3. package/lib/errors.d.ts +2 -0
  4. package/lib/errors.js +7 -3
  5. package/lib/errors.js.map +1 -1
  6. package/lib/index.d.ts +8 -2
  7. package/lib/index.js.map +1 -1
  8. package/lib/worker-tuner.d.ts +111 -1
  9. package/package.json +3 -3
  10. package/releases/aarch64-apple-darwin/index.node +0 -0
  11. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  12. package/releases/x86_64-apple-darwin/index.node +0 -0
  13. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  14. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  15. package/sdk-core/.github/workflows/per-pr.yml +3 -3
  16. package/sdk-core/Cargo.toml +0 -1
  17. package/sdk-core/client/Cargo.toml +1 -2
  18. package/sdk-core/client/src/lib.rs +21 -13
  19. package/sdk-core/client/src/metrics.rs +1 -1
  20. package/sdk-core/client/src/raw.rs +46 -1
  21. package/sdk-core/core/Cargo.toml +7 -7
  22. package/sdk-core/core/benches/workflow_replay.rs +1 -1
  23. package/sdk-core/core/src/abstractions/take_cell.rs +1 -1
  24. package/sdk-core/core/src/abstractions.rs +98 -10
  25. package/sdk-core/core/src/core_tests/activity_tasks.rs +8 -2
  26. package/sdk-core/core/src/core_tests/local_activities.rs +1 -1
  27. package/sdk-core/core/src/core_tests/mod.rs +3 -3
  28. package/sdk-core/core/src/core_tests/updates.rs +104 -9
  29. package/sdk-core/core/src/core_tests/workers.rs +72 -3
  30. package/sdk-core/core/src/core_tests/workflow_tasks.rs +6 -7
  31. package/sdk-core/core/src/debug_client.rs +78 -0
  32. package/sdk-core/core/src/ephemeral_server/mod.rs +15 -5
  33. package/sdk-core/core/src/lib.rs +30 -4
  34. package/sdk-core/core/src/pollers/mod.rs +1 -1
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +7 -7
  36. package/sdk-core/core/src/replay/mod.rs +4 -4
  37. package/sdk-core/core/src/telemetry/log_export.rs +2 -2
  38. package/sdk-core/core/src/telemetry/metrics.rs +69 -1
  39. package/sdk-core/core/src/telemetry/otel.rs +2 -2
  40. package/sdk-core/core/src/test_help/mod.rs +3 -3
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +3 -3
  42. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +1 -1
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +68 -24
  44. package/sdk-core/core/src/worker/activities.rs +26 -15
  45. package/sdk-core/core/src/worker/client/mocks.rs +10 -4
  46. package/sdk-core/core/src/worker/client.rs +17 -0
  47. package/sdk-core/core/src/worker/mod.rs +71 -13
  48. package/sdk-core/core/src/worker/slot_provider.rs +5 -7
  49. package/sdk-core/core/src/worker/tuner/fixed_size.rs +4 -3
  50. package/sdk-core/core/src/worker/tuner/resource_based.rs +171 -32
  51. package/sdk-core/core/src/worker/tuner.rs +18 -6
  52. package/sdk-core/core/src/worker/workflow/history_update.rs +43 -13
  53. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +6 -6
  54. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +6 -5
  55. package/sdk-core/core/src/worker/workflow/managed_run.rs +3 -3
  56. package/sdk-core/core/src/worker/workflow/mod.rs +13 -7
  57. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +7 -7
  58. package/sdk-core/core/src/worker/workflow/wft_poller.rs +2 -2
  59. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +11 -11
  60. package/sdk-core/core-api/Cargo.toml +1 -0
  61. package/sdk-core/core-api/src/worker.rs +84 -30
  62. package/sdk-core/sdk/Cargo.toml +1 -2
  63. package/sdk-core/sdk/src/lib.rs +1 -1
  64. package/sdk-core/sdk/src/workflow_context.rs +9 -8
  65. package/sdk-core/sdk/src/workflow_future.rs +19 -14
  66. package/sdk-core/sdk-core-protos/Cargo.toml +2 -0
  67. package/sdk-core/sdk-core-protos/build.rs +6 -1
  68. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +1 -0
  69. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +3207 -158
  70. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +2934 -118
  71. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/activity/v1/message.proto +67 -0
  72. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +47 -1
  73. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -7
  74. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  75. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +5 -3
  76. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +3 -3
  77. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +14 -13
  78. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -3
  79. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +22 -0
  80. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +13 -2
  81. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +26 -6
  82. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +5 -0
  83. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +6 -0
  84. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +46 -12
  85. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/update/v1/message.proto +18 -19
  86. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +27 -0
  87. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +192 -19
  88. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +279 -12
  89. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -1
  90. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -1
  91. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -1
  92. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/common/common.proto +1 -1
  93. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/core_interface.proto +17 -1
  94. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -1
  95. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +1 -1
  96. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -1
  97. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +1 -1
  98. package/sdk-core/sdk-core-protos/src/lib.rs +30 -6
  99. package/sdk-core/test-utils/Cargo.toml +1 -2
  100. package/sdk-core/test-utils/src/lib.rs +2 -2
  101. package/sdk-core/tests/heavy_tests.rs +1 -1
  102. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +2 -2
  103. package/sdk-core/tests/integ_tests/metrics_tests.rs +144 -7
  104. package/sdk-core/tests/integ_tests/queries_tests.rs +1 -1
  105. package/sdk-core/tests/integ_tests/update_tests.rs +109 -5
  106. package/sdk-core/tests/integ_tests/worker_tests.rs +44 -8
  107. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1 -1
  108. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
  109. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +1 -1
  110. package/sdk-core/tests/integ_tests/workflow_tests.rs +3 -2
  111. package/src/conversions/slot_supplier_bridge.rs +287 -0
  112. package/src/conversions.rs +23 -15
  113. package/src/helpers.rs +35 -1
  114. package/src/runtime.rs +7 -3
  115. package/src/worker.rs +1 -1
  116. package/ts/errors.ts +9 -2
  117. package/ts/index.ts +19 -4
  118. package/ts/worker-tuner.ts +123 -1
  119. package/sdk-core/sdk-core-protos/protos/api_upstream/.gitmodules +0 -3
@@ -25,7 +25,7 @@ use crate::{
25
25
  };
26
26
  use activity_heartbeat_manager::ActivityHeartbeatManager;
27
27
  use dashmap::DashMap;
28
- use futures::{
28
+ use futures_util::{
29
29
  stream,
30
30
  stream::{BoxStream, PollNext},
31
31
  Stream, StreamExt,
@@ -37,14 +37,14 @@ use std::{
37
37
  atomic::{AtomicBool, Ordering},
38
38
  Arc,
39
39
  },
40
- time::{Duration, Instant},
40
+ time::{Duration, Instant, SystemTime},
41
41
  };
42
- use temporal_sdk_core_api::worker::{ActivitySlotInfo, ActivitySlotKind};
42
+ use temporal_sdk_core_api::worker::ActivitySlotKind;
43
43
  use temporal_sdk_core_protos::{
44
44
  coresdk::{
45
45
  activity_result::{self as ar, activity_execution_result as aer},
46
46
  activity_task::{ActivityCancelReason, ActivityTask},
47
- ActivityHeartbeat,
47
+ ActivityHeartbeat, ActivitySlotInfo,
48
48
  },
49
49
  temporal::api::{
50
50
  failure::v1::{failure::FailureInfo, ApplicationFailureInfo, CanceledFailureInfo, Failure},
@@ -93,6 +93,7 @@ struct InFlightActInfo {
93
93
  /// Only kept for logging reasons
94
94
  workflow_run_id: String,
95
95
  start_time: Instant,
96
+ scheduled_time: Option<SystemTime>,
96
97
  }
97
98
 
98
99
  /// Augments [InFlightActInfo] with details specific to remote activities
@@ -127,6 +128,7 @@ impl RemoteInFlightActInfo {
127
128
  workflow_id: wec.workflow_id,
128
129
  workflow_run_id: wec.run_id,
129
130
  start_time: Instant::now(),
131
+ scheduled_time: poll_resp.scheduled_time.and_then(|i| i.try_into().ok()),
130
132
  },
131
133
  heartbeat_timeout: poll_resp.heartbeat_timeout,
132
134
  issued_cancel_to_lang: None,
@@ -180,7 +182,7 @@ enum ActivityTaskSource {
180
182
  impl WorkerActivityTasks {
181
183
  #[allow(clippy::too_many_arguments)]
182
184
  pub(crate) fn new(
183
- semaphore: Arc<MeteredPermitDealer<ActivitySlotKind>>,
185
+ semaphore: MeteredPermitDealer<ActivitySlotKind>,
184
186
  poller: BoxedActPoller,
185
187
  client: Arc<dyn WorkerClient>,
186
188
  metrics: MetricsContext,
@@ -194,7 +196,7 @@ impl WorkerActivityTasks {
194
196
  let server_poller_stream =
195
197
  new_activity_task_poller(poller, metrics.clone(), shutdown_initiated_token.clone());
196
198
  let (eager_activities_tx, eager_activities_rx) = unbounded_channel();
197
- let eager_activities_semaphore = ClosableMeteredPermitDealer::new_arc(semaphore);
199
+ let eager_activities_semaphore = ClosableMeteredPermitDealer::new_arc(Arc::new(semaphore));
198
200
 
199
201
  let start_tasks_stream_complete = CancellationToken::new();
200
202
  let starts_stream = Self::merge_start_task_sources(
@@ -277,7 +279,7 @@ impl WorkerActivityTasks {
277
279
  // Prefer eager activities over polling the server
278
280
  stream::select_with_strategy(non_poll_stream, poller_stream, |_: &mut ()| PollNext::Left)
279
281
  .map(|res| Some(res.map_err(Into::into)))
280
- .chain(futures::stream::once(async move {
282
+ .chain(futures_util::stream::once(async move {
281
283
  on_complete_token.cancel();
282
284
  None
283
285
  }))
@@ -336,10 +338,19 @@ impl WorkerActivityTasks {
336
338
  let _flushing_guard = self.completers_lock.read().await;
337
339
  let maybe_net_err = match status {
338
340
  aer::Status::WillCompleteAsync(_) => None,
339
- aer::Status::Completed(ar::Success { result }) => client
340
- .complete_activity_task(task_token.clone(), result.map(Into::into))
341
- .await
342
- .err(),
341
+ aer::Status::Completed(ar::Success { result }) => {
342
+ if let Some(sched_time) = act_info
343
+ .base
344
+ .scheduled_time
345
+ .and_then(|st| st.elapsed().ok())
346
+ {
347
+ act_metrics.act_execution_succeeded(sched_time);
348
+ }
349
+ client
350
+ .complete_activity_task(task_token.clone(), result.map(Into::into))
351
+ .await
352
+ .err()
353
+ }
343
354
  aer::Status::Failed(ar::Failure { failure }) => {
344
355
  act_metrics.act_execution_failed();
345
356
  client
@@ -542,7 +553,7 @@ where
542
553
  outstanding_entry.insert(RemoteInFlightActInfo::new(
543
554
  &task.resp,
544
555
  task.permit.into_used(ActivitySlotInfo {
545
- activity_type: activity_type_name,
556
+ activity_type: activity_type_name.to_string(),
546
557
  }),
547
558
  ));
548
559
  // If we have already waited the grace period and issued cancels,
@@ -739,7 +750,7 @@ mod tests {
739
750
  .times(2)
740
751
  .returning(|_, _| Ok(Default::default()));
741
752
  let mock_client = Arc::new(mock_client);
742
- let sem = Arc::new(fixed_size_permit_dealer(10));
753
+ let sem = fixed_size_permit_dealer(10);
743
754
  let shutdown_token = CancellationToken::new();
744
755
  let ap = new_activity_task_buffer(
745
756
  mock_client.clone(),
@@ -828,7 +839,7 @@ mod tests {
828
839
  })
829
840
  });
830
841
  let mock_client = Arc::new(mock_client);
831
- let sem = Arc::new(fixed_size_permit_dealer(1));
842
+ let sem = fixed_size_permit_dealer(1);
832
843
  let shutdown_token = CancellationToken::new();
833
844
  let ap = new_activity_task_buffer(
834
845
  mock_client.clone(),
@@ -899,7 +910,7 @@ mod tests {
899
910
  .times(2)
900
911
  .returning(|_, _| Ok(Default::default()));
901
912
  let mock_client = Arc::new(mock_client);
902
- let sem = Arc::new(fixed_size_permit_dealer(1));
913
+ let sem = fixed_size_permit_dealer(1);
903
914
  let shutdown_token = CancellationToken::new();
904
915
  let ap = new_activity_task_buffer(
905
916
  mock_client.clone(),
@@ -1,11 +1,11 @@
1
1
  use super::*;
2
- use futures::Future;
3
- use once_cell::sync::Lazy;
2
+ use futures_util::Future;
4
3
  use std::sync::Arc;
4
+ use std::sync::LazyLock;
5
5
  use temporal_client::SlotManager;
6
6
 
7
- static DEFAULT_WORKERS_REGISTRY: Lazy<Arc<SlotManager>> =
8
- Lazy::new(|| Arc::new(SlotManager::new()));
7
+ pub(crate) static DEFAULT_WORKERS_REGISTRY: LazyLock<Arc<SlotManager>> =
8
+ LazyLock::new(|| Arc::new(SlotManager::new()));
9
9
 
10
10
  pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
11
11
  signal_and_query_header: true,
@@ -18,6 +18,7 @@ pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
18
18
  eager_workflow_start: true,
19
19
  sdk_metadata: true,
20
20
  count_group_by_execution_status: false,
21
+ nexus: false,
21
22
  };
22
23
 
23
24
  #[cfg(test)]
@@ -29,6 +30,8 @@ pub(crate) fn mock_workflow_client() -> MockWorkerClient {
29
30
  r.expect_workers()
30
31
  .returning(|| DEFAULT_WORKERS_REGISTRY.clone());
31
32
  r.expect_is_mock().returning(|| true);
33
+ r.expect_shutdown_worker()
34
+ .returning(|_| Ok(ShutdownWorkerResponse {}));
32
35
  r
33
36
  }
34
37
 
@@ -119,6 +122,9 @@ mockall::mock! {
119
122
  impl Future<Output = Result<DescribeNamespaceResponse>> + Send + 'b
120
123
  where 'a: 'b, Self: 'b;
121
124
 
125
+ fn shutdown_worker<'a, 'b>(&self, sticky_task_queue: String) -> impl Future<Output = Result<ShutdownWorkerResponse>> + Send + 'b
126
+ where 'a: 'b, Self: 'b;
127
+
122
128
  fn replace_client(&self, new_client: RetryClient<Client>);
123
129
  fn capabilities(&self) -> Option<Capabilities>;
124
130
  fn workers(&self) -> Arc<SlotManager>;
@@ -146,6 +146,7 @@ pub(crate) trait WorkerClient: Sync + Send {
146
146
  query_result: QueryResult,
147
147
  ) -> Result<RespondQueryTaskCompletedResponse>;
148
148
  async fn describe_namespace(&self) -> Result<DescribeNamespaceResponse>;
149
+ async fn shutdown_worker(&self, sticky_task_queue: String) -> Result<ShutdownWorkerResponse>;
149
150
 
150
151
  fn replace_client(&self, new_client: RetryClient<Client>);
151
152
  fn capabilities(&self) -> Option<Capabilities>;
@@ -232,6 +233,7 @@ impl WorkerClient for WorkerClientBag {
232
233
  namespace: self.namespace.clone(),
233
234
  sdk_metadata: Some(request.sdk_metadata),
234
235
  metering_metadata: Some(request.metering_metadata),
236
+ capabilities: None,
235
237
  };
236
238
  Ok(self
237
239
  .cloned_client()
@@ -384,6 +386,21 @@ impl WorkerClient for WorkerClientBag {
384
386
  .await
385
387
  }
386
388
 
389
+ async fn shutdown_worker(&self, sticky_task_queue: String) -> Result<ShutdownWorkerResponse> {
390
+ let request = ShutdownWorkerRequest {
391
+ namespace: self.namespace.clone(),
392
+ identity: self.identity.clone(),
393
+ sticky_task_queue,
394
+ reason: "graceful shutdown".to_string(),
395
+ };
396
+
397
+ Ok(
398
+ WorkflowService::shutdown_worker(&mut self.cloned_client(), request)
399
+ .await?
400
+ .into_inner(),
401
+ )
402
+ }
403
+
387
404
  fn replace_client(&self, new_client: RetryClient<Client>) {
388
405
  let mut replaceable_client = self.replaceable_client.write();
389
406
  *replaceable_client = new_client;
@@ -26,8 +26,8 @@ use crate::{
26
26
  protosext::validate_activity_completion,
27
27
  telemetry::{
28
28
  metrics::{
29
- activity_poller, activity_worker_type, local_activity_worker_type, workflow_poller,
30
- workflow_sticky_poller, workflow_worker_type, MetricsContext,
29
+ activity_poller, activity_worker_type, workflow_poller, workflow_sticky_poller,
30
+ workflow_worker_type, MetricsContext,
31
31
  },
32
32
  TelemetryInstance,
33
33
  },
@@ -49,6 +49,7 @@ use std::{
49
49
  atomic::{AtomicBool, Ordering},
50
50
  Arc,
51
51
  },
52
+ time::Duration,
52
53
  };
53
54
  use temporal_client::{ConfiguredClient, TemporalServiceClientWithMetrics, WorkerKey};
54
55
  use temporal_sdk_core_protos::{
@@ -66,10 +67,13 @@ use temporal_sdk_core_protos::{
66
67
  },
67
68
  TaskToken,
68
69
  };
69
- use tokio::sync::mpsc::unbounded_channel;
70
+ use tokio::sync::{mpsc::unbounded_channel, watch};
70
71
  use tokio_stream::wrappers::UnboundedReceiverStream;
71
72
  use tokio_util::sync::CancellationToken;
72
73
 
74
+ use crate::{
75
+ abstractions::PermitDealerContextData, telemetry::metrics::local_activity_worker_type,
76
+ };
73
77
  use temporal_sdk_core_api::errors::WorkerValidationError;
74
78
  #[cfg(test)]
75
79
  use {
@@ -77,7 +81,7 @@ use {
77
81
  pollers::{BoxedPoller, MockPermittedPollBuffer},
78
82
  protosext::ValidPollWFTQResponse,
79
83
  },
80
- futures::stream::BoxStream,
84
+ futures_util::stream::BoxStream,
81
85
  temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse,
82
86
  };
83
87
 
@@ -102,6 +106,22 @@ pub struct Worker {
102
106
  non_local_activities_complete: Arc<AtomicBool>,
103
107
  /// Set when local activities are complete and should stop being polled
104
108
  local_activities_complete: Arc<AtomicBool>,
109
+ /// Used to track all permits have been released
110
+ all_permits_tracker: tokio::sync::Mutex<AllPermitsTracker>,
111
+ }
112
+
113
+ struct AllPermitsTracker {
114
+ wft_permits: watch::Receiver<usize>,
115
+ act_permits: watch::Receiver<usize>,
116
+ la_permits: watch::Receiver<usize>,
117
+ }
118
+
119
+ impl AllPermitsTracker {
120
+ async fn all_done(&mut self) {
121
+ let _ = self.wft_permits.wait_for(|x| *x == 0).await;
122
+ let _ = self.act_permits.wait_for(|x| *x == 0).await;
123
+ let _ = self.la_permits.wait_for(|x| *x == 0).await;
124
+ }
105
125
  }
106
126
 
107
127
  #[async_trait::async_trait]
@@ -270,7 +290,12 @@ impl Worker {
270
290
  tuner.attach_metrics(meter.clone());
271
291
  }
272
292
  let shutdown_token = CancellationToken::new();
273
- let wft_slots = Arc::new(MeteredPermitDealer::new(
293
+ let slot_context_data = Arc::new(PermitDealerContextData {
294
+ task_queue: config.task_queue.clone(),
295
+ worker_identity: config.client_identity_override.clone().unwrap_or_default(),
296
+ worker_build_id: config.worker_build_id.clone(),
297
+ });
298
+ let wft_slots = MeteredPermitDealer::new(
274
299
  tuner.workflow_task_slot_supplier(),
275
300
  metrics.with_new_attrs([workflow_worker_type()]),
276
301
  if config.max_cached_workflows > 0 {
@@ -280,12 +305,16 @@ impl Worker {
280
305
  } else {
281
306
  None
282
307
  },
283
- ));
284
- let act_slots = Arc::new(MeteredPermitDealer::new(
308
+ slot_context_data.clone(),
309
+ );
310
+ let wft_permits = wft_slots.get_extant_count_rcv();
311
+ let act_slots = MeteredPermitDealer::new(
285
312
  tuner.activity_task_slot_supplier(),
286
313
  metrics.with_new_attrs([activity_worker_type()]),
287
314
  None,
288
- ));
315
+ slot_context_data.clone(),
316
+ );
317
+ let act_permits = act_slots.get_extant_count_rcv();
289
318
  let (external_wft_tx, external_wft_rx) = unbounded_channel();
290
319
  let (wft_stream, act_poller) = match task_pollers {
291
320
  TaskPollers::Real => {
@@ -320,7 +349,7 @@ impl Worker {
320
349
  normal_name: config.task_queue.clone(),
321
350
  },
322
351
  max_sticky_polls,
323
- wft_slots.clone(),
352
+ wft_slots.clone().into_sticky(),
324
353
  shutdown_token.child_token(),
325
354
  Some(move |np| {
326
355
  sticky_metrics.record_num_pollers(np);
@@ -366,7 +395,8 @@ impl Worker {
366
395
  wft_stream,
367
396
  act_poller,
368
397
  } => {
369
- let ap = act_poller.map(|ap| MockPermittedPollBuffer::new(act_slots.clone(), ap));
398
+ let ap = act_poller
399
+ .map(|ap| MockPermittedPollBuffer::new(Arc::new(act_slots.clone()), ap));
370
400
  let wft_semaphore = wft_slots.clone();
371
401
  let wfs = wft_stream.then(move |s| {
372
402
  let wft_semaphore = wft_semaphore.clone();
@@ -381,11 +411,18 @@ impl Worker {
381
411
  };
382
412
 
383
413
  let (hb_tx, hb_rx) = unbounded_channel();
384
- let local_act_mgr = Arc::new(LocalActivityManager::new(
414
+ let la_pemit_dealer = MeteredPermitDealer::new(
385
415
  tuner.local_activity_slot_supplier(),
416
+ metrics.with_new_attrs([local_activity_worker_type()]),
417
+ None,
418
+ slot_context_data,
419
+ );
420
+ let la_permits = la_pemit_dealer.get_extant_count_rcv();
421
+ let local_act_mgr = Arc::new(LocalActivityManager::new(
386
422
  config.namespace.clone(),
423
+ la_pemit_dealer,
387
424
  hb_tx,
388
- metrics.with_new_attrs([local_activity_worker_type()]),
425
+ metrics.clone(),
389
426
  ));
390
427
  let at_task_mgr = act_poller.map(|ap| {
391
428
  WorkerActivityTasks::new(
@@ -453,6 +490,11 @@ impl Worker {
453
490
  // Non-local activities are already complete if configured not to poll for them.
454
491
  non_local_activities_complete: Arc::new(AtomicBool::new(!poll_on_non_local_activities)),
455
492
  local_activities_complete: Default::default(),
493
+ all_permits_tracker: tokio::sync::Mutex::new(AllPermitsTracker {
494
+ wft_permits,
495
+ act_permits,
496
+ la_permits,
497
+ }),
456
498
  }
457
499
  }
458
500
 
@@ -460,6 +502,15 @@ impl Worker {
460
502
  /// completed
461
503
  async fn shutdown(&self) {
462
504
  self.initiate_shutdown();
505
+ if let Some(name) = self.workflows.get_sticky_queue_name() {
506
+ // This is a best effort call and we can still shutdown the worker if it fails
507
+ match self.client.shutdown_worker(name).await {
508
+ Err(err) if err.code() != tonic::Code::Unavailable => {
509
+ warn!("Failed to shutdown sticky queue {:?}", err);
510
+ }
511
+ _ => {}
512
+ }
513
+ }
463
514
  // We need to wait for all local activities to finish so no more workflow task heartbeats
464
515
  // will be generated
465
516
  self.local_act_mgr
@@ -474,6 +525,13 @@ impl Worker {
474
525
  if let Some(acts) = self.at_task_mgr.as_ref() {
475
526
  acts.shutdown().await;
476
527
  }
528
+ // Wait for all permits to be released, but don't totally hang real-world shutdown.
529
+ tokio::select! {
530
+ _ = async { self.all_permits_tracker.lock().await.all_done().await } => {},
531
+ _ = tokio::time::sleep(Duration::from_secs(1)) => {
532
+ dbg_panic!("Waiting for all slot permits to release took too long!");
533
+ }
534
+ };
477
535
  }
478
536
 
479
537
  /// Finish shutting down by consuming the background pollers and freeing all resources
@@ -750,7 +808,7 @@ mod tests {
750
808
  use crate::{
751
809
  advance_fut, test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client,
752
810
  };
753
- use futures::FutureExt;
811
+ use futures_util::FutureExt;
754
812
 
755
813
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
756
814
 
@@ -7,8 +7,6 @@ use crate::{
7
7
  protosext::ValidPollWFTQResponse,
8
8
  worker::workflow::wft_poller::validate_wft,
9
9
  };
10
-
11
- use std::sync::Arc;
12
10
  use temporal_client::{Slot as SlotTrait, SlotProvider as SlotProviderTrait};
13
11
  use temporal_sdk_core_api::worker::WorkflowSlotKind;
14
12
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollWorkflowTaskQueueResponse;
@@ -58,7 +56,7 @@ impl SlotTrait for Slot {
58
56
  pub(super) struct SlotProvider {
59
57
  namespace: String,
60
58
  task_queue: String,
61
- wft_semaphore: Arc<MeteredPermitDealer<WorkflowSlotKind>>,
59
+ wft_semaphore: MeteredPermitDealer<WorkflowSlotKind>,
62
60
  external_wft_tx: WFTStreamSender,
63
61
  }
64
62
 
@@ -66,7 +64,7 @@ impl SlotProvider {
66
64
  pub(super) fn new(
67
65
  namespace: String,
68
66
  task_queue: String,
69
- wft_semaphore: Arc<MeteredPermitDealer<WorkflowSlotKind>>,
67
+ wft_semaphore: MeteredPermitDealer<WorkflowSlotKind>,
70
68
  external_wft_tx: WFTStreamSender,
71
69
  ) -> Self {
72
70
  Self {
@@ -119,7 +117,7 @@ mod tests {
119
117
 
120
118
  #[tokio::test]
121
119
  async fn slot_propagates_through_channel() {
122
- let wft_semaphore = Arc::new(fixed_size_permit_dealer(2));
120
+ let wft_semaphore = fixed_size_permit_dealer(2);
123
121
  let (external_wft_tx, mut external_wft_rx) = unbounded_channel();
124
122
 
125
123
  let provider = SlotProvider::new(
@@ -142,7 +140,7 @@ mod tests {
142
140
  let (external_wft_tx, mut external_wft_rx) = unbounded_channel();
143
141
  {
144
142
  let external_wft_tx = external_wft_tx;
145
- let wft_semaphore = Arc::new(fixed_size_permit_dealer(2));
143
+ let wft_semaphore = fixed_size_permit_dealer(2);
146
144
  let provider = SlotProvider::new(
147
145
  "my_namespace".to_string(),
148
146
  "my_queue".to_string(),
@@ -156,7 +154,7 @@ mod tests {
156
154
 
157
155
  #[tokio::test]
158
156
  async fn unused_slots_reclaimed() {
159
- let wft_semaphore = Arc::new(fixed_size_permit_dealer(2));
157
+ let wft_semaphore = fixed_size_permit_dealer(2);
160
158
  {
161
159
  let wft_semaphore = wft_semaphore.clone();
162
160
  let (external_wft_tx, _) = unbounded_channel();
@@ -1,6 +1,7 @@
1
1
  use std::{marker::PhantomData, sync::Arc};
2
2
  use temporal_sdk_core_api::worker::{
3
- SlotKind, SlotReservationContext, SlotSupplier, SlotSupplierPermit,
3
+ SlotKind, SlotMarkUsedContext, SlotReleaseContext, SlotReservationContext, SlotSupplier,
4
+ SlotSupplierPermit,
4
5
  };
5
6
  use tokio::sync::Semaphore;
6
7
 
@@ -42,9 +43,9 @@ where
42
43
  perm.ok().map(SlotSupplierPermit::with_user_data)
43
44
  }
44
45
 
45
- fn mark_slot_used(&self, _info: SK::Info<'_>) {}
46
+ fn mark_slot_used(&self, _ctx: &dyn SlotMarkUsedContext<SlotKind = Self::SlotKind>) {}
46
47
 
47
- fn release_slot(&self) {}
48
+ fn release_slot(&self, _ctx: &dyn SlotReleaseContext<SlotKind = Self::SlotKind>) {}
48
49
 
49
50
  fn available_slots(&self) -> Option<usize> {
50
51
  Some(self.sem.available_permits())