@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
@@ -12,6 +12,8 @@ extern crate tracing;
12
12
  extern crate core;
13
13
 
14
14
  mod abstractions;
15
+ #[cfg(feature = "debug-plugin")]
16
+ pub mod debug_client;
15
17
  #[cfg(feature = "ephemeral-server")]
16
18
  pub mod ephemeral_server;
17
19
  mod internal_flags;
@@ -54,7 +56,7 @@ use crate::{
54
56
  worker::client::WorkerClientBag,
55
57
  };
56
58
  use anyhow::bail;
57
- use futures::Stream;
59
+ use futures_util::Stream;
58
60
  use std::sync::Arc;
59
61
  use temporal_client::{ConfiguredClient, TemporalServiceClientWithMetrics};
60
62
  use temporal_sdk_core_api::{
@@ -198,6 +200,23 @@ pub struct CoreRuntime {
198
200
  runtime_handle: tokio::runtime::Handle,
199
201
  }
200
202
 
203
+ /// Wraps a [tokio::runtime::Builder] to allow layering multiple on_thread_start functions
204
+ pub struct TokioRuntimeBuilder<F> {
205
+ /// The underlying tokio runtime builder
206
+ pub inner: tokio::runtime::Builder,
207
+ /// A function to be called when setting the runtime builder's on thread start
208
+ pub lang_on_thread_start: Option<F>,
209
+ }
210
+
211
+ impl Default for TokioRuntimeBuilder<Box<dyn Fn() + Send + Sync>> {
212
+ fn default() -> Self {
213
+ TokioRuntimeBuilder {
214
+ inner: tokio::runtime::Builder::new_multi_thread(),
215
+ lang_on_thread_start: None,
216
+ }
217
+ }
218
+ }
219
+
201
220
  impl CoreRuntime {
202
221
  /// Create a new core runtime with the provided telemetry options and tokio runtime builder.
203
222
  /// Also initialize telemetry for the thread this is being called on.
@@ -212,18 +231,25 @@ impl CoreRuntime {
212
231
  /// # Panics
213
232
  /// If a tokio runtime has already been initialized. To re-use an existing runtime, call
214
233
  /// [CoreRuntime::new_assume_tokio].
215
- pub fn new(
234
+ pub fn new<F>(
216
235
  telemetry_options: TelemetryOptions,
217
- mut tokio_builder: tokio::runtime::Builder,
218
- ) -> Result<Self, anyhow::Error> {
236
+ mut tokio_builder: TokioRuntimeBuilder<F>,
237
+ ) -> Result<Self, anyhow::Error>
238
+ where
239
+ F: Fn() + Send + Sync + 'static,
240
+ {
219
241
  let telemetry = telemetry_init(telemetry_options)?;
220
242
  let subscriber = telemetry.trace_subscriber();
221
243
  let runtime = tokio_builder
244
+ .inner
222
245
  .enable_all()
223
246
  .on_thread_start(move || {
224
247
  if let Some(sub) = subscriber.as_ref() {
225
248
  set_trace_subscriber_for_current_thread(sub.clone());
226
249
  }
250
+ if let Some(lang_on_thread_start) = tokio_builder.lang_on_thread_start.as_ref() {
251
+ lang_on_thread_start();
252
+ }
227
253
  })
228
254
  .build()?;
229
255
  let _rg = runtime.enter();
@@ -14,7 +14,7 @@ use temporal_sdk_core_protos::temporal::api::workflowservice::v1::{
14
14
  };
15
15
 
16
16
  #[cfg(test)]
17
- use futures::Future;
17
+ use futures_util::Future;
18
18
  #[cfg(test)]
19
19
  pub(crate) use poll_buffer::MockPermittedPollBuffer;
20
20
  use temporal_sdk_core_api::worker::{ActivitySlotKind, WorkflowSlotKind};
@@ -3,8 +3,7 @@ use crate::{
3
3
  pollers::{self, Poller},
4
4
  worker::client::WorkerClient,
5
5
  };
6
- use futures::{prelude::stream::FuturesUnordered, StreamExt};
7
- use futures_util::{future::BoxFuture, FutureExt};
6
+ use futures_util::{future::BoxFuture, stream::FuturesUnordered, FutureExt, StreamExt};
8
7
  use governor::{Quota, RateLimiter};
9
8
  use std::{
10
9
  fmt::Debug,
@@ -73,7 +72,7 @@ where
73
72
  {
74
73
  pub(crate) fn new<FT, DelayFut>(
75
74
  poll_fn: impl Fn() -> FT + Send + Sync + 'static,
76
- permit_dealer: Arc<MeteredPermitDealer<SK>>,
75
+ permit_dealer: MeteredPermitDealer<SK>,
77
76
  max_pollers: usize,
78
77
  shutdown: CancellationToken,
79
78
  num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
@@ -85,6 +84,7 @@ where
85
84
  {
86
85
  let (tx, rx) = unbounded_channel();
87
86
  let (starter, wait_for_start) = broadcast::channel(1);
87
+ let permit_dealer = Arc::new(permit_dealer);
88
88
  let active_pollers = Arc::new(AtomicUsize::new(0));
89
89
  let join_handles = FuturesUnordered::new();
90
90
  let pf = Arc::new(poll_fn);
@@ -243,7 +243,7 @@ pub(crate) fn new_workflow_task_buffer(
243
243
  client: Arc<dyn WorkerClient>,
244
244
  task_queue: TaskQueue,
245
245
  concurrent_pollers: usize,
246
- permit_dealer: Arc<MeteredPermitDealer<WorkflowSlotKind>>,
246
+ permit_dealer: MeteredPermitDealer<WorkflowSlotKind>,
247
247
  shutdown: CancellationToken,
248
248
  num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
249
249
  ) -> PollWorkflowTaskBuffer {
@@ -268,7 +268,7 @@ pub(crate) fn new_activity_task_buffer(
268
268
  client: Arc<dyn WorkerClient>,
269
269
  task_queue: String,
270
270
  concurrent_pollers: usize,
271
- semaphore: Arc<MeteredPermitDealer<ActivitySlotKind>>,
271
+ semaphore: MeteredPermitDealer<ActivitySlotKind>,
272
272
  max_tps: Option<f64>,
273
273
  shutdown: CancellationToken,
274
274
  num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
@@ -337,7 +337,7 @@ mod tests {
337
337
  abstractions::tests::fixed_size_permit_dealer,
338
338
  worker::client::mocks::mock_manual_workflow_client,
339
339
  };
340
- use futures::FutureExt;
340
+ use futures_util::FutureExt;
341
341
  use std::time::Duration;
342
342
  use temporal_sdk_core_protos::temporal::api::enums::v1::TaskQueueKind;
343
343
  use tokio::{select, sync::mpsc::channel};
@@ -364,7 +364,7 @@ mod tests {
364
364
  normal_name: "".to_string(),
365
365
  },
366
366
  1,
367
- Arc::new(fixed_size_permit_dealer(10)),
367
+ fixed_size_permit_dealer(10),
368
368
  CancellationToken::new(),
369
369
  None::<fn(usize)>,
370
370
  );
@@ -9,9 +9,9 @@ use crate::{
9
9
  },
10
10
  Worker,
11
11
  };
12
- use futures::{FutureExt, Stream, StreamExt};
13
- use once_cell::sync::OnceCell;
12
+ use futures_util::{FutureExt, Stream, StreamExt};
14
13
  use parking_lot::Mutex;
14
+ use std::sync::OnceLock;
15
15
  use std::{
16
16
  pin::Pin,
17
17
  sync::Arc,
@@ -179,7 +179,7 @@ impl Stream for HistoryFeederStream {
179
179
  pub(crate) struct Historator {
180
180
  iter: Pin<Box<dyn Stream<Item = HistoryForReplay> + Send>>,
181
181
  allow_stream: UnboundedReceiverStream<String>,
182
- worker_closer: Arc<OnceCell<CancellationToken>>,
182
+ worker_closer: Arc<OnceLock<CancellationToken>>,
183
183
  dat: Arc<Mutex<HistoratorDat>>,
184
184
  replay_done_tx: UnboundedSender<String>,
185
185
  }
@@ -192,7 +192,7 @@ impl Historator {
192
192
  Self {
193
193
  iter: Box::pin(histories.fuse()),
194
194
  allow_stream: UnboundedReceiverStream::new(replay_done_rx),
195
- worker_closer: Arc::new(OnceCell::new()),
195
+ worker_closer: Arc::new(OnceLock::new()),
196
196
  dat,
197
197
  replay_done_tx,
198
198
  }
@@ -1,4 +1,4 @@
1
- use futures::channel::mpsc::{channel, Receiver, Sender};
1
+ use futures_channel::mpsc::{channel, Receiver, Sender};
2
2
  use parking_lot::Mutex;
3
3
  use ringbuf::{consumer::Consumer, producer::Producer, traits::Split, HeapRb};
4
4
  use std::{collections::HashMap, fmt, sync::Arc, time::SystemTime};
@@ -219,7 +219,7 @@ mod tests {
219
219
  telemetry::{construct_filter_string, CoreLogStreamConsumer},
220
220
  telemetry_init,
221
221
  };
222
- use futures::stream::StreamExt;
222
+ use futures_util::stream::StreamExt;
223
223
  use std::{
224
224
  fmt,
225
225
  sync::{Arc, Mutex},
@@ -42,6 +42,12 @@ struct Instruments {
42
42
  act_execution_failed: Arc<dyn Counter>,
43
43
  act_sched_to_start_latency: Arc<dyn HistogramDuration>,
44
44
  act_exec_latency: Arc<dyn HistogramDuration>,
45
+ act_exec_succeeded_latency: Arc<dyn HistogramDuration>,
46
+ la_execution_cancelled: Arc<dyn Counter>,
47
+ la_execution_failed: Arc<dyn Counter>,
48
+ la_exec_latency: Arc<dyn HistogramDuration>,
49
+ la_exec_succeeded_latency: Arc<dyn HistogramDuration>,
50
+ la_total: Arc<dyn Counter>,
45
51
  worker_registered: Arc<dyn Counter>,
46
52
  num_pollers: Arc<dyn Gauge>,
47
53
  task_slots_available: Arc<dyn Gauge>,
@@ -177,6 +183,13 @@ impl MetricsContext {
177
183
  self.instruments.act_execution_failed.add(1, &self.kvs);
178
184
  }
179
185
 
186
+ /// Record end-to-end (sched-to-complete) time for successful activity executions
187
+ pub(crate) fn act_execution_succeeded(&self, dur: Duration) {
188
+ self.instruments
189
+ .act_exec_succeeded_latency
190
+ .record(dur, &self.kvs);
191
+ }
192
+
180
193
  /// Record activity task schedule to start time in millis
181
194
  pub(crate) fn act_sched_to_start_latency(&self, dur: Duration) {
182
195
  self.instruments
@@ -190,6 +203,28 @@ impl MetricsContext {
190
203
  self.instruments.act_exec_latency.record(dur, &self.kvs);
191
204
  }
192
205
 
206
+ pub(crate) fn la_execution_cancelled(&self) {
207
+ self.instruments.la_execution_cancelled.add(1, &self.kvs);
208
+ }
209
+
210
+ pub(crate) fn la_execution_failed(&self) {
211
+ self.instruments.la_execution_failed.add(1, &self.kvs);
212
+ }
213
+
214
+ pub(crate) fn la_exec_latency(&self, dur: Duration) {
215
+ self.instruments.la_exec_latency.record(dur, &self.kvs);
216
+ }
217
+
218
+ pub(crate) fn la_exec_succeeded_latency(&self, dur: Duration) {
219
+ self.instruments
220
+ .la_exec_succeeded_latency
221
+ .record(dur, &self.kvs);
222
+ }
223
+
224
+ pub(crate) fn la_executed(&self) {
225
+ self.instruments.la_total.add(1, &self.kvs);
226
+ }
227
+
193
228
  /// A worker was registered
194
229
  pub(crate) fn worker_registered(&self) {
195
230
  self.instruments.worker_registered.add(1, &self.kvs);
@@ -316,6 +351,39 @@ impl Instruments {
316
351
  unit: "duration".into(),
317
352
  description: "Histogram of activity execution latencies".into(),
318
353
  }),
354
+ act_exec_succeeded_latency: meter.histogram_duration(MetricParameters {
355
+ name: "activity_succeed_endtoend_latency".into(),
356
+ unit: "duration".into(),
357
+ description: "Histogram of activity execution latencies for successful activities"
358
+ .into(),
359
+ }),
360
+ la_execution_cancelled: meter.counter(MetricParameters {
361
+ name: "local_activity_execution_cancelled".into(),
362
+ description: "Count of local activity executions that were cancelled".into(),
363
+ unit: "".into(),
364
+ }),
365
+ la_execution_failed: meter.counter(MetricParameters {
366
+ name: "local_activity_execution_failed".into(),
367
+ description: "Count of local activity executions that failed".into(),
368
+ unit: "".into(),
369
+ }),
370
+ la_exec_latency: meter.histogram_duration(MetricParameters {
371
+ name: "local_activity_execution_latency".into(),
372
+ unit: "duration".into(),
373
+ description: "Histogram of local activity execution latencies".into(),
374
+ }),
375
+ la_exec_succeeded_latency: meter.histogram_duration(MetricParameters {
376
+ name: "local_activity_succeed_endtoend_latency".into(),
377
+ unit: "duration".into(),
378
+ description:
379
+ "Histogram of local activity execution latencies for successful local activities"
380
+ .into(),
381
+ }),
382
+ la_total: meter.counter(MetricParameters {
383
+ name: "local_activity_total".into(),
384
+ description: "Count of local activities executed".into(),
385
+ unit: "".into(),
386
+ }),
319
387
  // name kept as worker start for compat with old sdk / what users expect
320
388
  worker_registered: meter.counter(MetricParameters {
321
389
  name: "worker_start".into(),
@@ -807,7 +875,7 @@ mod tests {
807
875
  a1.set(Arc::new(DummyCustomAttrs(1))).unwrap();
808
876
  // Verify all metrics are created. This number will need to get updated any time a metric
809
877
  // is added.
810
- let num_metrics = 24;
878
+ let num_metrics = 30;
811
879
  #[allow(clippy::needless_range_loop)] // Sorry clippy, this reads easier.
812
880
  for metric_num in 1..=num_metrics {
813
881
  let hole = assert_matches!(&events[metric_num],
@@ -335,9 +335,9 @@ impl GaugeF64 for MemoryGauge<f64> {
335
335
  }
336
336
 
337
337
  fn default_resource_instance() -> &'static Resource {
338
- use once_cell::sync::OnceCell;
338
+ use std::sync::OnceLock;
339
339
 
340
- static INSTANCE: OnceCell<Resource> = OnceCell::new();
340
+ static INSTANCE: OnceLock<Resource> = OnceLock::new();
341
341
  INSTANCE.get_or_init(|| {
342
342
  let resource = Resource::default();
343
343
  if resource.get(Key::from("service.name")) == Some(Value::from("unknown_service")) {
@@ -15,7 +15,7 @@ use crate::{
15
15
  };
16
16
  use async_trait::async_trait;
17
17
  use bimap::BiMap;
18
- use futures::{future::BoxFuture, stream, stream::BoxStream, FutureExt, Stream, StreamExt};
18
+ use futures_util::{future::BoxFuture, stream, stream::BoxStream, FutureExt, Stream, StreamExt};
19
19
  use mockall::TimesRange;
20
20
  use parking_lot::RwLock;
21
21
  use std::{
@@ -1014,9 +1014,9 @@ macro_rules! job_assert {
1014
1014
  #[macro_export]
1015
1015
  macro_rules! advance_fut {
1016
1016
  ($fut:ident) => {
1017
- ::futures::pin_mut!($fut);
1017
+ ::futures_util::pin_mut!($fut);
1018
1018
  {
1019
- let waker = ::futures::task::noop_waker();
1019
+ let waker = ::futures_util::task::noop_waker();
1020
1020
  let mut cx = core::task::Context::from_waker(&waker);
1021
1021
  for _ in 0..10 {
1022
1022
  assert_matches!($fut.poll_unpin(&mut cx), core::task::Poll::Pending);
@@ -3,7 +3,7 @@ use crate::{
3
3
  worker::{activities::PendingActivityCancel, client::WorkerClient},
4
4
  TaskToken,
5
5
  };
6
- use futures::StreamExt;
6
+ use futures_util::StreamExt;
7
7
  use std::{
8
8
  collections::{hash_map::Entry, HashMap},
9
9
  sync::Arc,
@@ -94,7 +94,7 @@ impl ActivityHeartbeatManager {
94
94
  // The stream of incoming heartbeats uses unfold to carry state across each item in the
95
95
  // stream. The closure checks if, for any given activity, we should heartbeat or not
96
96
  // depending on its delay and when we last issued a heartbeat for it.
97
- futures::stream::unfold(heartbeat_stream_state, move |mut hb_states| {
97
+ futures_util::stream::unfold(heartbeat_stream_state, move |mut hb_states| {
98
98
  async move {
99
99
  let hb = tokio::select! {
100
100
  biased;
@@ -140,7 +140,7 @@ impl ActivityHeartbeatManager {
140
140
  .record_activity_heartbeat(tt.clone(), details.into_payloads())
141
141
  .await
142
142
  {
143
- Ok(RecordActivityTaskHeartbeatResponse { cancel_requested }) => {
143
+ Ok(RecordActivityTaskHeartbeatResponse { cancel_requested, activity_paused: _ }) => {
144
144
  if cancel_requested {
145
145
  cancels_tx
146
146
  .send(PendingActivityCancel::new(
@@ -1,5 +1,5 @@
1
1
  use crate::{pollers::BoxedActPoller, worker::activities::PermittedTqResp, MetricsContext};
2
- use futures::{stream, Stream};
2
+ use futures_util::{stream, Stream};
3
3
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
4
4
  use tokio::select;
5
5
  use tokio_util::sync::CancellationToken;
@@ -2,25 +2,27 @@ use crate::{
2
2
  abstractions::{dbg_panic, MeteredPermitDealer, OwnedMeteredSemPermit, UsedMeteredSemPermit},
3
3
  protosext::ValidScheduleLA,
4
4
  retry_logic::RetryPolicyExt,
5
+ telemetry::metrics::{activity_type, workflow_type},
5
6
  worker::workflow::HeartbeatTimeoutMsg,
6
7
  MetricsContext, TaskToken,
7
8
  };
8
- use futures::{stream::BoxStream, Stream};
9
- use futures_util::{future, future::AbortRegistration, stream, StreamExt};
9
+ use futures_util::{
10
+ future, future::AbortRegistration, stream, stream::BoxStream, Stream, StreamExt,
11
+ };
10
12
  use parking_lot::{Mutex, MutexGuard};
11
13
  use std::{
12
14
  collections::{hash_map::Entry, HashMap},
13
15
  fmt::{Debug, Formatter},
14
16
  pin::Pin,
15
- sync::Arc,
16
17
  task::{Context, Poll},
17
18
  time::{Duration, Instant, SystemTime},
18
19
  };
19
- use temporal_sdk_core_api::worker::{LocalActivitySlotInfo, LocalActivitySlotKind, SlotSupplier};
20
+ use temporal_sdk_core_api::worker::LocalActivitySlotKind;
20
21
  use temporal_sdk_core_protos::{
21
22
  coresdk::{
22
23
  activity_result::{Cancellation, Failure as ActFail, Success},
23
24
  activity_task::{activity_task, ActivityCancelReason, ActivityTask, Cancel, Start},
25
+ LocalActivitySlotInfo,
24
26
  },
25
27
  temporal::api::{
26
28
  common::v1::WorkflowExecution,
@@ -173,6 +175,9 @@ pub(crate) struct LocalActivityManager {
173
175
  rcvs: tokio::sync::Mutex<RcvChans>,
174
176
  shutdown_complete_tok: CancellationToken,
175
177
  dat: Mutex<LAMData>,
178
+ /// Note that these metrics do *not* include the `worker_type` label, as every metric
179
+ /// emitted here is already specific to local activities via the metric name.
180
+ metrics: MetricsContext,
176
181
  }
177
182
 
178
183
  struct LocalActivityInfo {
@@ -205,20 +210,19 @@ impl LAMData {
205
210
 
206
211
  impl LocalActivityManager {
207
212
  pub(crate) fn new(
208
- slot_supplier: Arc<dyn SlotSupplier<SlotKind = LocalActivitySlotKind> + Send + Sync>,
209
213
  namespace: String,
214
+ permit_dealer: MeteredPermitDealer<LocalActivitySlotKind>,
210
215
  heartbeat_timeout_tx: UnboundedSender<HeartbeatTimeoutMsg>,
211
216
  metrics_context: MetricsContext,
212
217
  ) -> Self {
213
218
  let (act_req_tx, act_req_rx) = unbounded_channel();
214
219
  let (cancels_req_tx, cancels_req_rx) = unbounded_channel();
215
220
  let shutdown_complete_tok = CancellationToken::new();
216
- let semaphore = MeteredPermitDealer::new(slot_supplier, metrics_context, None);
217
221
  Self {
218
222
  namespace,
219
223
  rcvs: tokio::sync::Mutex::new(RcvChans::new(
220
224
  act_req_rx,
221
- semaphore,
225
+ permit_dealer,
222
226
  cancels_req_rx,
223
227
  shutdown_complete_tok.clone(),
224
228
  )),
@@ -233,16 +237,28 @@ impl LocalActivityManager {
233
237
  next_tt_num: 0,
234
238
  }),
235
239
  workflows_have_shut_down: Default::default(),
240
+ metrics: metrics_context,
236
241
  }
237
242
  }
238
243
 
239
244
  #[cfg(test)]
240
245
  fn test(max_concurrent: usize) -> Self {
241
246
  use crate::worker::tuner::FixedSizeSlotSupplier;
247
+ use std::sync::Arc;
242
248
 
243
249
  let ss = Arc::new(FixedSizeSlotSupplier::new(max_concurrent));
244
250
  let (hb_tx, _hb_rx) = unbounded_channel();
245
- Self::new(ss, "fake_ns".to_string(), hb_tx, MetricsContext::no_op())
251
+ Self::new(
252
+ "fake_ns".to_string(),
253
+ MeteredPermitDealer::new(
254
+ ss,
255
+ MetricsContext::no_op(),
256
+ None,
257
+ Arc::new(Default::default()),
258
+ ),
259
+ hb_tx,
260
+ MetricsContext::no_op(),
261
+ )
246
262
  }
247
263
 
248
264
  #[cfg(test)]
@@ -473,12 +489,18 @@ impl LocalActivityManager {
473
489
  dispatch_time: Instant::now(),
474
490
  attempt,
475
491
  _permit: permit.into_used(LocalActivitySlotInfo {
476
- activity_type: new_la.workflow_type.as_str(),
492
+ activity_type: new_la.workflow_type.clone(),
477
493
  }),
478
494
  },
479
495
  );
480
496
 
481
497
  let (schedule_to_close, start_to_close) = sa.close_timeouts.into_sched_and_start();
498
+ self.metrics
499
+ .with_new_attrs([
500
+ activity_type(sa.activity_type.clone()),
501
+ workflow_type(new_la.workflow_type.clone()),
502
+ ])
503
+ .la_executed();
482
504
  Some(NextPendingLAAction::Dispatch(ActivityTask {
483
505
  task_token: tt.0,
484
506
  variant: Some(activity_task::Variant::Start(Start {
@@ -500,9 +522,7 @@ impl LocalActivityManager {
500
522
  .ok(),
501
523
  start_to_close_timeout: start_to_close
502
524
  .or(schedule_to_close)
503
- .unwrap()
504
- .try_into()
505
- .ok(),
525
+ .and_then(|t| t.try_into().ok()),
506
526
  heartbeat_timeout: None,
507
527
  retry_policy: Some(sa.retry_policy),
508
528
  is_local: true,
@@ -536,6 +556,11 @@ impl LocalActivityManager {
536
556
  }
537
557
  }
538
558
 
559
+ let la_metrics = self.metrics.with_new_attrs([
560
+ activity_type(info.la_info.schedule_cmd.activity_type.clone()),
561
+ workflow_type(info.la_info.workflow_type.clone()),
562
+ ]);
563
+
539
564
  enum Outcome {
540
565
  FailurePath { backoff: Option<Duration> },
541
566
  JustReport,
@@ -553,31 +578,48 @@ impl LocalActivityManager {
553
578
  }
554
579
 
555
580
  let mut is_timeout = false;
581
+ let runtime = info.dispatch_time.elapsed();
582
+ la_metrics.la_exec_latency(runtime);
556
583
  let outcome = match &status {
557
- LocalActivityExecutionResult::Failed(fail) => Outcome::FailurePath {
558
- backoff: calc_backoff!(fail),
559
- },
560
- LocalActivityExecutionResult::TimedOut(fail)
561
- if matches!(status.get_timeout_type(), Some(TimeoutType::StartToClose)) =>
562
- {
563
- // Start to close timeouts are retryable, other timeout types aren't.
564
- is_timeout = true;
584
+ LocalActivityExecutionResult::Failed(fail) => {
585
+ la_metrics.la_execution_failed();
565
586
  Outcome::FailurePath {
566
587
  backoff: calc_backoff!(fail),
567
588
  }
568
589
  }
569
- LocalActivityExecutionResult::TimedOut(_) => {
590
+ LocalActivityExecutionResult::TimedOut(fail) => {
591
+ la_metrics.la_execution_failed();
570
592
  is_timeout = true;
593
+ // Start to close timeouts are retryable, other timeout types aren't.
594
+ if matches!(status.get_timeout_type(), Some(TimeoutType::StartToClose)) {
595
+ Outcome::FailurePath {
596
+ backoff: calc_backoff!(fail),
597
+ }
598
+ } else {
599
+ Outcome::JustReport
600
+ }
601
+ }
602
+ LocalActivityExecutionResult::Completed(_) => {
603
+ if let Some(rt) = info
604
+ .la_info
605
+ .schedule_cmd
606
+ .original_schedule_time
607
+ .and_then(|t| t.elapsed().ok())
608
+ {
609
+ la_metrics.la_exec_succeeded_latency(rt);
610
+ }
611
+ Outcome::JustReport
612
+ }
613
+ LocalActivityExecutionResult::Cancelled { .. } => {
614
+ la_metrics.la_execution_cancelled();
571
615
  Outcome::JustReport
572
616
  }
573
- LocalActivityExecutionResult::Completed(_)
574
- | LocalActivityExecutionResult::Cancelled { .. } => Outcome::JustReport,
575
617
  };
576
618
 
577
619
  let mut resolution = LocalActivityResolution {
578
620
  seq: info.la_info.schedule_cmd.seq,
579
621
  result: status,
580
- runtime: info.dispatch_time.elapsed(),
622
+ runtime,
581
623
  attempt: info.attempt,
582
624
  backoff: None,
583
625
  original_schedule_time: info.la_info.schedule_cmd.original_schedule_time,
@@ -692,6 +734,8 @@ impl LocalActivityManager {
692
734
  while !self.set_shutdown_complete_if_ready(&mut self.dat.lock()) {
693
735
  self.complete_notify.notified().await;
694
736
  }
737
+ // This makes sure we drop any permits that might be held inside the stream
738
+ self.rcvs.lock().await.inner = stream::empty().boxed();
695
739
  }
696
740
 
697
741
  /// Try to close the activity stream as soon as worker shutdown is initiated. This is required