@temporalio/core-bridge 1.3.0 → 1.4.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 (36) hide show
  1. package/Cargo.lock +117 -212
  2. package/index.d.ts +8 -2
  3. package/package.json +2 -3
  4. package/releases/aarch64-apple-darwin/index.node +0 -0
  5. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  6. package/releases/x86_64-apple-darwin/index.node +0 -0
  7. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  8. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  9. package/sdk-core/.buildkite/pipeline.yml +2 -0
  10. package/sdk-core/.cargo/config.toml +1 -1
  11. package/sdk-core/bridge-ffi/src/lib.rs +2 -2
  12. package/sdk-core/client/Cargo.toml +1 -1
  13. package/sdk-core/client/src/lib.rs +16 -11
  14. package/sdk-core/client/src/metrics.rs +13 -11
  15. package/sdk-core/client/src/raw.rs +1 -2
  16. package/sdk-core/client/src/retry.rs +57 -42
  17. package/sdk-core/core/Cargo.toml +12 -8
  18. package/sdk-core/core/src/core_tests/activity_tasks.rs +65 -40
  19. package/sdk-core/core/src/ephemeral_server/mod.rs +19 -3
  20. package/sdk-core/core/src/lib.rs +2 -2
  21. package/sdk-core/core/src/pollers/mod.rs +2 -0
  22. package/sdk-core/core/src/telemetry/metrics.rs +48 -39
  23. package/sdk-core/core/src/telemetry/mod.rs +53 -22
  24. package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
  25. package/sdk-core/core/src/worker/client/mocks.rs +1 -0
  26. package/sdk-core/core/src/worker/workflow/mod.rs +4 -1
  27. package/sdk-core/core-api/Cargo.toml +1 -1
  28. package/sdk-core/test-utils/src/lib.rs +21 -2
  29. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +27 -40
  30. package/sdk-core/tests/integ_tests/polling_tests.rs +1 -0
  31. package/sdk-core/tests/integ_tests/queries_tests.rs +1 -1
  32. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +1 -5
  33. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +8 -2
  34. package/sdk-core/tests/main.rs +7 -0
  35. package/sdk-core/tests/runner.rs +93 -0
  36. package/src/conversions.rs +19 -3
@@ -1,15 +1,17 @@
1
1
  use super::TELEM_SERVICE_NAME;
2
2
  use crate::telemetry::GLOBAL_TELEM_DAT;
3
+ use opentelemetry::sdk::metrics::aggregators::Aggregator;
4
+ use opentelemetry::sdk::metrics::sdk_api::{Descriptor, InstrumentKind};
3
5
  use opentelemetry::{
4
6
  global,
5
- metrics::{Counter, Descriptor, InstrumentKind, Meter, ValueRecorder},
7
+ metrics::{Counter, Histogram, Meter},
6
8
  sdk::{
7
- export::metrics::{Aggregator, AggregatorSelector},
9
+ export::metrics::AggregatorSelector,
8
10
  metrics::aggregators::{histogram, last_value, sum},
9
11
  },
10
- KeyValue,
12
+ Context, KeyValue,
11
13
  };
12
- use std::{borrow::Cow, sync::Arc, time::Duration};
14
+ use std::{sync::Arc, time::Duration};
13
15
 
14
16
  /// Used to track context associated with metrics, and record/update them
15
17
  ///
@@ -17,12 +19,16 @@ use std::{borrow::Cow, sync::Arc, time::Duration};
17
19
  /// appropriate k/vs have already been set.
18
20
  #[derive(Default, Clone, Debug)]
19
21
  pub(crate) struct MetricsContext {
22
+ ctx: Context,
20
23
  kvs: Arc<Vec<KeyValue>>,
21
24
  }
22
25
 
23
26
  impl MetricsContext {
24
27
  fn new(kvs: Vec<KeyValue>) -> Self {
25
- Self { kvs: Arc::new(kvs) }
28
+ Self {
29
+ ctx: Context::current(),
30
+ kvs: Arc::new(kvs),
31
+ }
26
32
  }
27
33
 
28
34
  pub(crate) fn top_level(namespace: String) -> Self {
@@ -38,113 +44,116 @@ impl MetricsContext {
38
44
  pub(crate) fn with_new_attrs(&self, new_kvs: impl IntoIterator<Item = KeyValue>) -> Self {
39
45
  let mut kvs = self.kvs.clone();
40
46
  Arc::make_mut(&mut kvs).extend(new_kvs);
41
- Self { kvs }
47
+ Self {
48
+ ctx: Context::current(),
49
+ kvs,
50
+ }
42
51
  }
43
52
 
44
53
  /// A workflow task queue poll succeeded
45
54
  pub(crate) fn wf_tq_poll_ok(&self) {
46
- WF_TASK_QUEUE_POLL_SUCCEED_COUNTER.add(1, &self.kvs);
55
+ WF_TASK_QUEUE_POLL_SUCCEED_COUNTER.add(&self.ctx, 1, &self.kvs);
47
56
  }
48
57
 
49
58
  /// A workflow task queue poll timed out / had empty response
50
59
  pub(crate) fn wf_tq_poll_empty(&self) {
51
- WF_TASK_QUEUE_POLL_EMPTY_COUNTER.add(1, &self.kvs);
60
+ WF_TASK_QUEUE_POLL_EMPTY_COUNTER.add(&self.ctx, 1, &self.kvs);
52
61
  }
53
62
 
54
63
  /// A workflow task execution failed
55
64
  pub(crate) fn wf_task_failed(&self) {
56
- WF_TASK_EXECUTION_FAILURE_COUNTER.add(1, &self.kvs);
65
+ WF_TASK_EXECUTION_FAILURE_COUNTER.add(&self.ctx, 1, &self.kvs);
57
66
  }
58
67
 
59
68
  /// A workflow completed successfully
60
69
  pub(crate) fn wf_completed(&self) {
61
- WF_COMPLETED_COUNTER.add(1, &self.kvs);
70
+ WF_COMPLETED_COUNTER.add(&self.ctx, 1, &self.kvs);
62
71
  }
63
72
 
64
73
  /// A workflow ended cancelled
65
74
  pub(crate) fn wf_canceled(&self) {
66
- WF_CANCELED_COUNTER.add(1, &self.kvs);
75
+ WF_CANCELED_COUNTER.add(&self.ctx, 1, &self.kvs);
67
76
  }
68
77
 
69
78
  /// A workflow ended failed
70
79
  pub(crate) fn wf_failed(&self) {
71
- WF_FAILED_COUNTER.add(1, &self.kvs);
80
+ WF_FAILED_COUNTER.add(&self.ctx, 1, &self.kvs);
72
81
  }
73
82
 
74
83
  /// A workflow continued as new
75
84
  pub(crate) fn wf_continued_as_new(&self) {
76
- WF_CONT_COUNTER.add(1, &self.kvs);
85
+ WF_CONT_COUNTER.add(&self.ctx, 1, &self.kvs);
77
86
  }
78
87
 
79
88
  /// Record workflow total execution time in milliseconds
80
89
  pub(crate) fn wf_e2e_latency(&self, dur: Duration) {
81
- WF_E2E_LATENCY.record(dur.as_millis() as u64, &self.kvs);
90
+ WF_E2E_LATENCY.record(&self.ctx, dur.as_millis() as u64, &self.kvs);
82
91
  }
83
92
 
84
93
  /// Record workflow task schedule to start time in millis
85
94
  pub(crate) fn wf_task_sched_to_start_latency(&self, dur: Duration) {
86
- WF_TASK_SCHED_TO_START_LATENCY.record(dur.as_millis() as u64, &self.kvs);
95
+ WF_TASK_SCHED_TO_START_LATENCY.record(&self.ctx, dur.as_millis() as u64, &self.kvs);
87
96
  }
88
97
 
89
98
  /// Record workflow task execution time in milliseconds
90
99
  pub(crate) fn wf_task_latency(&self, dur: Duration) {
91
- WF_TASK_EXECUTION_LATENCY.record(dur.as_millis() as u64, &self.kvs);
100
+ WF_TASK_EXECUTION_LATENCY.record(&self.ctx, dur.as_millis() as u64, &self.kvs);
92
101
  }
93
102
 
94
103
  /// Record time it takes to catch up on replaying a WFT
95
104
  pub(crate) fn wf_task_replay_latency(&self, dur: Duration) {
96
- WF_TASK_REPLAY_LATENCY.record(dur.as_millis() as u64, &self.kvs);
105
+ WF_TASK_REPLAY_LATENCY.record(&self.ctx, dur.as_millis() as u64, &self.kvs);
97
106
  }
98
107
 
99
108
  /// An activity long poll timed out
100
109
  pub(crate) fn act_poll_timeout(&self) {
101
- ACT_POLL_NO_TASK.add(1, &self.kvs);
110
+ ACT_POLL_NO_TASK.add(&self.ctx, 1, &self.kvs);
102
111
  }
103
112
 
104
113
  /// An activity execution failed
105
114
  pub(crate) fn act_execution_failed(&self) {
106
- ACT_EXECUTION_FAILED.add(1, &self.kvs);
115
+ ACT_EXECUTION_FAILED.add(&self.ctx, 1, &self.kvs);
107
116
  }
108
117
 
109
118
  /// Record activity task schedule to start time in millis
110
119
  pub(crate) fn act_sched_to_start_latency(&self, dur: Duration) {
111
- ACT_SCHED_TO_START_LATENCY.record(dur.as_millis() as u64, &self.kvs);
120
+ ACT_SCHED_TO_START_LATENCY.record(&self.ctx, dur.as_millis() as u64, &self.kvs);
112
121
  }
113
122
 
114
123
  /// Record time it took to complete activity execution, from the time core generated the
115
124
  /// activity task, to the time lang responded with a completion (failure or success).
116
125
  pub(crate) fn act_execution_latency(&self, dur: Duration) {
117
- ACT_EXEC_LATENCY.record(dur.as_millis() as u64, &self.kvs);
126
+ ACT_EXEC_LATENCY.record(&self.ctx, dur.as_millis() as u64, &self.kvs);
118
127
  }
119
128
 
120
129
  /// A worker was registered
121
130
  pub(crate) fn worker_registered(&self) {
122
- WORKER_REGISTERED.add(1, &self.kvs);
131
+ WORKER_REGISTERED.add(&self.ctx, 1, &self.kvs);
123
132
  }
124
133
 
125
134
  /// Record current number of available task slots. Context should have worker type set.
126
135
  pub(crate) fn available_task_slots(&self, num: usize) {
127
- TASK_SLOTS_AVAILABLE.record(num as u64, &self.kvs)
136
+ TASK_SLOTS_AVAILABLE.record(&self.ctx, num as u64, &self.kvs)
128
137
  }
129
138
 
130
139
  /// Record current number of pollers. Context should include poller type / task queue tag.
131
140
  pub(crate) fn record_num_pollers(&self, num: usize) {
132
- NUM_POLLERS.record(num as u64, &self.kvs);
141
+ NUM_POLLERS.record(&self.ctx, num as u64, &self.kvs);
133
142
  }
134
143
 
135
144
  /// A workflow task found a cached workflow to run against
136
145
  pub(crate) fn sticky_cache_hit(&self) {
137
- STICKY_CACHE_HIT.add(1, &self.kvs);
146
+ STICKY_CACHE_HIT.add(&self.ctx, 1, &self.kvs);
138
147
  }
139
148
 
140
149
  /// A workflow task did not find a cached workflow
141
150
  pub(crate) fn sticky_cache_miss(&self) {
142
- STICKY_CACHE_MISS.add(1, &self.kvs);
151
+ STICKY_CACHE_MISS.add(&self.ctx, 1, &self.kvs);
143
152
  }
144
153
 
145
154
  /// Record current cache size (in number of wfs, not bytes)
146
155
  pub(crate) fn cache_size(&self, size: u64) {
147
- STICKY_CACHE_SIZE.record(size, &self.kvs);
156
+ STICKY_CACHE_SIZE.record(&self.ctx, size, &self.kvs);
148
157
  }
149
158
  }
150
159
 
@@ -182,8 +191,8 @@ macro_rules! tm {
182
191
  };
183
192
  (vr_u64, $ident:ident, $name:expr) => {
184
193
  lazy_static::lazy_static! {
185
- static ref $ident: ValueRecorder<u64> = {
186
- METRIC_METER.u64_value_recorder(metric_prefix().to_string() + $name).init()
194
+ static ref $ident: Histogram<u64> = {
195
+ METRIC_METER.u64_histogram(metric_prefix().to_string() + $name).init()
187
196
  };
188
197
  }
189
198
  };
@@ -214,22 +223,22 @@ pub(crate) fn activity_type(ty: String) -> KeyValue {
214
223
  pub(crate) fn workflow_type(ty: String) -> KeyValue {
215
224
  KeyValue::new(KEY_WF_TYPE, ty)
216
225
  }
217
- pub(crate) const fn workflow_worker_type() -> KeyValue {
226
+ pub(crate) fn workflow_worker_type() -> KeyValue {
218
227
  KeyValue {
219
228
  key: opentelemetry::Key::from_static_str(KEY_WORKER_TYPE),
220
- value: opentelemetry::Value::String(Cow::Borrowed("WorkflowWorker")),
229
+ value: opentelemetry::Value::String("WorkflowWorker".into()),
221
230
  }
222
231
  }
223
- pub(crate) const fn activity_worker_type() -> KeyValue {
232
+ pub(crate) fn activity_worker_type() -> KeyValue {
224
233
  KeyValue {
225
234
  key: opentelemetry::Key::from_static_str(KEY_WORKER_TYPE),
226
- value: opentelemetry::Value::String(Cow::Borrowed("ActivityWorker")),
235
+ value: opentelemetry::Value::String("ActivityWorker".into()),
227
236
  }
228
237
  }
229
- pub(crate) const fn local_activity_worker_type() -> KeyValue {
238
+ pub(crate) fn local_activity_worker_type() -> KeyValue {
230
239
  KeyValue {
231
240
  key: opentelemetry::Key::from_static_str(KEY_WORKER_TYPE),
232
- value: opentelemetry::Value::String(Cow::Borrowed("LocalActivityWorker")),
241
+ value: opentelemetry::Value::String("LocalActivityWorker".into()),
233
242
  }
234
243
  }
235
244
 
@@ -338,12 +347,12 @@ pub struct SDKAggSelector;
338
347
 
339
348
  impl AggregatorSelector for SDKAggSelector {
340
349
  fn aggregator_for(&self, descriptor: &Descriptor) -> Option<Arc<dyn Aggregator + Send + Sync>> {
341
- // Observers are always last value
342
- if *descriptor.instrument_kind() == InstrumentKind::ValueObserver {
350
+ // Gauges are always last value
351
+ if *descriptor.instrument_kind() == InstrumentKind::GaugeObserver {
343
352
  return Some(Arc::new(last_value()));
344
353
  }
345
354
 
346
- if *descriptor.instrument_kind() == InstrumentKind::ValueRecorder {
355
+ if *descriptor.instrument_kind() == InstrumentKind::Histogram {
347
356
  let dname = descriptor
348
357
  .name()
349
358
  .strip_prefix(metric_prefix())
@@ -366,7 +375,7 @@ impl AggregatorSelector for SDKAggSelector {
366
375
  ACT_EXEC_LATENCY_NAME => ACT_EXE_MS_BUCKETS,
367
376
  _ => DEFAULT_MS_BUCKETS,
368
377
  };
369
- return Some(Arc::new(histogram(descriptor, buckets)));
378
+ return Some(Arc::new(histogram(buckets)));
370
379
  }
371
380
 
372
381
  Some(Arc::new(sum()))
@@ -12,8 +12,12 @@ use once_cell::sync::OnceCell;
12
12
  use opentelemetry::{
13
13
  global,
14
14
  metrics::Meter,
15
- sdk::{metrics::PushController, trace::Config, Resource},
16
- util::tokio_interval_stream,
15
+ runtime,
16
+ sdk::{
17
+ export::metrics::aggregation::{self, Temporality, TemporalitySelector},
18
+ trace::Config,
19
+ Resource,
20
+ },
17
21
  KeyValue,
18
22
  };
19
23
  use opentelemetry_otlp::WithExportConfig;
@@ -25,6 +29,7 @@ use std::{
25
29
  time::Duration,
26
30
  };
27
31
  use temporal_sdk_core_api::CoreTelemetry;
32
+ use tonic::metadata::MetadataMap;
28
33
  use tracing_subscriber::{filter::ParseError, layer::SubscriberExt, EnvFilter};
29
34
  use url::Url;
30
35
 
@@ -68,7 +73,7 @@ pub enum MetricsExporter {
68
73
  }
69
74
 
70
75
  /// Control where logs go
71
- #[derive(Debug, Clone)]
76
+ #[derive(Debug, Clone, Copy)]
72
77
  pub enum Logger {
73
78
  /// Log directly to console.
74
79
  Console,
@@ -100,6 +105,33 @@ pub struct TelemetryOptions {
100
105
  /// the prefix is consistent with other SDKs.
101
106
  #[builder(default)]
102
107
  pub no_temporal_prefix_for_metrics: bool,
108
+
109
+ /// Specifies the aggregation temporality for metric export. Defaults to cumulative.
110
+ #[builder(default = "MetricTemporality::Cumulative")]
111
+ pub metric_temporality: MetricTemporality,
112
+ }
113
+
114
+ /// Types of aggregation temporality for metric export.
115
+ /// See: <https://github.com/open-telemetry/opentelemetry-specification/blob/ce50e4634efcba8da445cc23523243cb893905cb/specification/metrics/datamodel.md#temporality>
116
+ #[derive(Debug, Clone, Copy)]
117
+ pub enum MetricTemporality {
118
+ /// Successive data points repeat the starting timestamp
119
+ Cumulative,
120
+ /// Successive data points advance the starting timestamp
121
+ Delta,
122
+ }
123
+
124
+ impl MetricTemporality {
125
+ fn to_selector(self) -> impl TemporalitySelector + Send + Sync + Clone {
126
+ match self {
127
+ MetricTemporality::Cumulative => {
128
+ aggregation::constant_temporality_selector(Temporality::Cumulative)
129
+ }
130
+ MetricTemporality::Delta => {
131
+ aggregation::constant_temporality_selector(Temporality::Delta)
132
+ }
133
+ }
134
+ }
103
135
  }
104
136
 
105
137
  impl TelemetryOptions {
@@ -122,7 +154,6 @@ impl Default for TelemetryOptions {
122
154
  /// Things that need to not be dropped while telemetry is ongoing
123
155
  #[derive(Default)]
124
156
  pub struct GlobalTelemDat {
125
- metric_push_controller: Option<PushController>,
126
157
  core_export_logger: Option<CoreExportLogger>,
127
158
  runtime: Option<tokio::runtime::Runtime>,
128
159
  prom_srv: Option<PromServer>,
@@ -201,7 +232,7 @@ pub fn telemetry_init(opts: &TelemetryOptions) -> Result<&'static GlobalTelemDat
201
232
  .pretty()
202
233
  .with_source_location(false);
203
234
  let reg = tracing_subscriber::registry()
204
- .with((&opts).try_get_env_filter()?)
235
+ .with((opts).try_get_env_filter()?)
205
236
  .with(
206
237
  tracing_subscriber::fmt::layer()
207
238
  .with_target(false)
@@ -220,31 +251,31 @@ pub fn telemetry_init(opts: &TelemetryOptions) -> Result<&'static GlobalTelemDat
220
251
  if let Some(ref metrics) = opts.metrics {
221
252
  match metrics {
222
253
  MetricsExporter::Prometheus(addr) => {
223
- let srv = PromServer::new(*addr)?;
254
+ let srv = PromServer::new(*addr, opts.metric_temporality.to_selector())?;
224
255
  globaldat.prom_srv = Some(srv);
225
256
  }
226
257
  MetricsExporter::Otel(OtelCollectorOptions { url, headers }) => {
227
258
  runtime.block_on(async {
228
259
  let metrics = opentelemetry_otlp::new_pipeline()
229
- .metrics(|f| runtime.spawn(f), tokio_interval_stream)
230
- .with_aggregator_selector(SDKAggSelector)
260
+ .metrics(
261
+ SDKAggSelector,
262
+ opts.metric_temporality.to_selector(),
263
+ runtime::Tokio,
264
+ )
231
265
  .with_period(Duration::from_secs(1))
232
- .with_resource(default_resource_kvs().iter().cloned())
266
+ .with_resource(default_resource())
233
267
  .with_exporter(
234
268
  // No joke exporter builder literally not cloneable for some insane
235
269
  // reason
236
270
  opentelemetry_otlp::new_exporter()
237
271
  .tonic()
238
272
  .with_endpoint(url.to_string())
239
- .with_metadata(
240
- tonic_otel::metadata::MetadataMap::from_headers(
241
- headers.try_into()?,
242
- ),
243
- ),
273
+ .with_metadata(MetadataMap::from_headers(
274
+ headers.try_into()?,
275
+ )),
244
276
  )
245
277
  .build()?;
246
- global::set_meter_provider(metrics.provider());
247
- globaldat.metric_push_controller = Some(metrics);
278
+ global::set_meter_provider(metrics);
248
279
  Result::<(), anyhow::Error>::Ok(())
249
280
  })?;
250
281
  }
@@ -262,14 +293,12 @@ pub fn telemetry_init(opts: &TelemetryOptions) -> Result<&'static GlobalTelemDat
262
293
  opentelemetry_otlp::new_exporter()
263
294
  .tonic()
264
295
  .with_endpoint(url.to_string())
265
- .with_metadata(
266
- tonic_otel::metadata::MetadataMap::from_headers(
267
- headers.try_into()?,
268
- ),
269
- ),
296
+ .with_metadata(MetadataMap::from_headers(
297
+ headers.try_into()?,
298
+ )),
270
299
  )
271
300
  .with_trace_config(tracer_cfg)
272
- .install_batch(opentelemetry::runtime::Tokio)?;
301
+ .install_batch(runtime::Tokio)?;
273
302
 
274
303
  let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer);
275
304
 
@@ -334,6 +363,7 @@ pub(crate) fn test_telem_console() {
334
363
  tracing: None,
335
364
  metrics: None,
336
365
  no_temporal_prefix_for_metrics: false,
366
+ metric_temporality: MetricTemporality::Cumulative,
337
367
  })
338
368
  .unwrap();
339
369
  }
@@ -350,6 +380,7 @@ pub(crate) fn test_telem_collector() {
350
380
  })),
351
381
  metrics: None,
352
382
  no_temporal_prefix_for_metrics: false,
383
+ metric_temporality: MetricTemporality::Cumulative,
353
384
  })
354
385
  .unwrap();
355
386
  }
@@ -1,13 +1,16 @@
1
- use crate::telemetry::{
2
- default_resource,
3
- metrics::{SDKAggSelector, DEFAULT_MS_BUCKETS},
4
- };
1
+ use crate::telemetry::{default_resource, metrics::SDKAggSelector};
5
2
  use hyper::{
6
3
  header::CONTENT_TYPE,
7
4
  service::{make_service_fn, service_fn},
8
5
  Body, Method, Request, Response, Server,
9
6
  };
10
- use opentelemetry::metrics::MetricsError;
7
+ use opentelemetry::{
8
+ metrics::MetricsError,
9
+ sdk::{
10
+ export::metrics::aggregation::TemporalitySelector,
11
+ metrics::{controllers, processors},
12
+ },
13
+ };
11
14
  use opentelemetry_prometheus::{ExporterBuilder, PrometheusExporter};
12
15
  use prometheus::{Encoder, TextEncoder};
13
16
  use std::{convert::Infallible, net::SocketAddr, sync::Arc};
@@ -19,14 +22,15 @@ pub(super) struct PromServer {
19
22
  }
20
23
 
21
24
  impl PromServer {
22
- pub fn new(addr: SocketAddr) -> Result<Self, MetricsError> {
23
- let exporter = ExporterBuilder::default()
24
- .with_default_histogram_boundaries(DEFAULT_MS_BUCKETS.to_vec())
25
- .with_aggregator_selector(SDKAggSelector)
26
- .with_host(addr.ip().to_string())
27
- .with_port(addr.port())
28
- .with_resource(default_resource())
29
- .try_init()?;
25
+ pub fn new(
26
+ addr: SocketAddr,
27
+ temporality: impl TemporalitySelector + Send + Sync + 'static,
28
+ ) -> Result<Self, MetricsError> {
29
+ let controller =
30
+ controllers::basic(processors::factory(SDKAggSelector, temporality).with_memory(true))
31
+ .with_resource(default_resource())
32
+ .build();
33
+ let exporter = ExporterBuilder::new(controller).try_init()?;
30
34
  Ok(Self {
31
35
  exporter: Arc::new(exporter),
32
36
  addr,
@@ -17,6 +17,7 @@ pub(crate) fn mock_manual_workflow_client() -> MockManualWorkerClient {
17
17
  // https://github.com/asomers/mockall/issues/189 to be fixed for it to go away.
18
18
  mockall::mock! {
19
19
  pub ManualWorkerClient {}
20
+ #[allow(unused)]
20
21
  impl WorkerClient for ManualWorkerClient {
21
22
  fn poll_workflow_task<'a, 'b>(&'a self, task_queue: String, is_sticky: bool)
22
23
  -> impl Future<Output = Result<PollWorkflowTaskQueueResponse>> + Send + 'b
@@ -78,6 +78,7 @@ use tokio_util::sync::CancellationToken;
78
78
  use tracing::Span;
79
79
 
80
80
  pub(crate) const LEGACY_QUERY_ID: &str = "legacy_query";
81
+ const MAX_EAGER_ACTIVITY_RESERVATIONS_PER_WORKFLOW_TASK: usize = 3;
81
82
 
82
83
  type Result<T, E = WFMachinesError> = result::Result<T, E>;
83
84
  type BoxedActivationStream = BoxStream<'static, Result<ActivationOrAuto, PollWfError>>;
@@ -472,7 +473,9 @@ impl Workflows {
472
473
  .as_ref()
473
474
  .map(|q| q.name == self.task_queue)
474
475
  .unwrap_or_default();
475
- if same_task_queue {
476
+ if same_task_queue
477
+ && reserved.len() < MAX_EAGER_ACTIVITY_RESERVATIONS_PER_WORKFLOW_TASK
478
+ {
476
479
  if let Some(p) = at_handle.reserve_slot() {
477
480
  reserved.push(p);
478
481
  } else {
@@ -17,7 +17,7 @@ anyhow = "1.0"
17
17
  async-trait = "0.1"
18
18
  derive_builder = "0.11"
19
19
  log = "0.4"
20
- opentelemetry = "0.17"
20
+ opentelemetry = "0.18"
21
21
  prost-types = "0.11"
22
22
  thiserror = "1.0"
23
23
  tonic = "0.8"
@@ -20,6 +20,7 @@ use temporal_client::{
20
20
  };
21
21
  use temporal_sdk::{interceptors::WorkerInterceptor, IntoActivityFunc, Worker, WorkflowFunction};
22
22
  use temporal_sdk_core::{
23
+ ephemeral_server::{EphemeralExe, EphemeralExeVersion},
23
24
  init_replay_worker, init_worker, telemetry_init, ClientOptions, ClientOptionsBuilder, Logger,
24
25
  MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
25
26
  TraceExporter, WorkerConfig, WorkerConfigBuilder,
@@ -40,11 +41,17 @@ use url::Url;
40
41
 
41
42
  pub const NAMESPACE: &str = "default";
42
43
  pub const TEST_Q: &str = "q";
44
+ /// The env var used to specify where the integ tests should point
45
+ pub const INTEG_SERVER_TARGET_ENV_VAR: &str = "TEMPORAL_SERVICE_ADDRESS";
46
+ /// This env var is set (to any value) if temporalite is in use
47
+ pub const INTEG_TEMPORALITE_USED_ENV_VAR: &str = "INTEG_TEMPORALITE_ON";
48
+ /// This env var is set (to any value) if the test server is in use
49
+ pub const INTEG_TEST_SERVER_USED_ENV_VAR: &str = "INTEG_TEST_SERVER_ON";
50
+
43
51
  /// If set, turn export traces and metrics to the OTel collector at the given URL
44
52
  const OTEL_URL_ENV_VAR: &str = "TEMPORAL_INTEG_OTEL_URL";
45
53
  /// If set, enable direct scraping of prom metrics on the specified port
46
54
  const PROM_ENABLE_ENV_VAR: &str = "TEMPORAL_INTEG_PROM_PORT";
47
-
48
55
  /// Create a worker instance which will use the provided test name to base the task queue and wf id
49
56
  /// upon. Returns the instance and the task queue name (which is also the workflow id).
50
57
  pub async fn init_core_and_create_wf(test_name: &str) -> CoreWfStarter {
@@ -433,8 +440,10 @@ impl WorkerInterceptor for TestWorkerCompletionIceptor {
433
440
  }
434
441
  }
435
442
 
443
+ /// Returns the client options used to connect to the server used for integration tests.
436
444
  pub fn get_integ_server_options() -> ClientOptions {
437
- let temporal_server_address = match env::var("TEMPORAL_SERVICE_ADDRESS") {
445
+ telemetry_init(&get_integ_telem_options()).expect("Telemetry inits cleanly");
446
+ let temporal_server_address = match env::var(INTEG_SERVER_TARGET_ENV_VAR) {
438
447
  Ok(addr) => addr,
439
448
  Err(_) => "http://localhost:7233".to_owned(),
440
449
  };
@@ -473,6 +482,16 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
473
482
  .unwrap()
474
483
  }
475
484
 
485
+ pub fn default_cached_download() -> EphemeralExe {
486
+ EphemeralExe::CachedDownload {
487
+ version: EphemeralExeVersion::Default {
488
+ sdk_name: "sdk-rust".to_string(),
489
+ sdk_version: "0.1.0".to_string(),
490
+ },
491
+ dest_dir: None,
492
+ }
493
+ }
494
+
476
495
  pub fn schedule_activity_cmd(
477
496
  seq: u32,
478
497
  task_q: &str,
@@ -1,14 +1,11 @@
1
1
  use std::time::{SystemTime, UNIX_EPOCH};
2
- use temporal_client::{TestService, WorkflowService};
3
- use temporal_sdk_core::{
4
- ephemeral_server::{
5
- EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporaliteConfigBuilder,
6
- TestServerConfigBuilder,
7
- },
8
- ClientOptions, ClientOptionsBuilder,
2
+ use temporal_client::{ClientOptionsBuilder, TestService, WorkflowService};
3
+ use temporal_sdk_core::ephemeral_server::{
4
+ EphemeralExe, EphemeralExeVersion, EphemeralServer, TemporaliteConfigBuilder,
5
+ TestServerConfigBuilder,
9
6
  };
10
7
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::DescribeNamespaceRequest;
11
- use temporal_sdk_core_test_utils::NAMESPACE;
8
+ use temporal_sdk_core_test_utils::{default_cached_download, NAMESPACE};
12
9
  use url::Url;
13
10
 
14
11
  #[tokio::test]
@@ -17,8 +14,9 @@ async fn temporalite_default() {
17
14
  .exe(default_cached_download())
18
15
  .build()
19
16
  .unwrap();
20
- let server = config.start_server().await.unwrap();
17
+ let mut server = config.start_server().await.unwrap();
21
18
  assert_ephemeral_server(&server).await;
19
+ server.shutdown().await.unwrap();
22
20
  }
23
21
 
24
22
  #[tokio::test]
@@ -27,8 +25,9 @@ async fn temporalite_fixed() {
27
25
  .exe(fixed_cached_download("v0.1.1"))
28
26
  .build()
29
27
  .unwrap();
30
- let server = config.start_server().await.unwrap();
28
+ let mut server = config.start_server().await.unwrap();
31
29
  assert_ephemeral_server(&server).await;
30
+ server.shutdown().await.unwrap();
32
31
  }
33
32
 
34
33
  #[tokio::test]
@@ -54,8 +53,9 @@ async fn test_server_default() {
54
53
  .exe(default_cached_download())
55
54
  .build()
56
55
  .unwrap();
57
- let server = config.start_server().await.unwrap();
56
+ let mut server = config.start_server().await.unwrap();
58
57
  assert_ephemeral_server(&server).await;
58
+ server.shutdown().await.unwrap();
59
59
  }
60
60
 
61
61
  #[tokio::test]
@@ -64,8 +64,9 @@ async fn test_server_fixed() {
64
64
  .exe(fixed_cached_download("v1.16.0"))
65
65
  .build()
66
66
  .unwrap();
67
- let server = config.start_server().await.unwrap();
67
+ let mut server = config.start_server().await.unwrap();
68
68
  assert_ephemeral_server(&server).await;
69
+ server.shutdown().await.unwrap();
69
70
  }
70
71
 
71
72
  #[tokio::test]
@@ -85,9 +86,22 @@ async fn test_server_shutdown_port_reuse() {
85
86
  server.shutdown().await.unwrap();
86
87
  }
87
88
 
89
+ fn fixed_cached_download(version: &str) -> EphemeralExe {
90
+ EphemeralExe::CachedDownload {
91
+ version: EphemeralExeVersion::Fixed(version.to_string()),
92
+ dest_dir: None,
93
+ }
94
+ }
95
+
88
96
  async fn assert_ephemeral_server(server: &EphemeralServer) {
89
97
  // Connect and describe namespace
90
- let mut client = client_options(&server.target)
98
+ let mut client = ClientOptionsBuilder::default()
99
+ .identity("integ_tester".to_string())
100
+ .target_url(Url::try_from(&*format!("http://{}", server.target)).unwrap())
101
+ .client_name("temporal-core".to_string())
102
+ .client_version("0.1.0".to_string())
103
+ .build()
104
+ .unwrap()
91
105
  .connect_no_namespace(None, None)
92
106
  .await
93
107
  .unwrap();
@@ -112,30 +126,3 @@ async fn assert_ephemeral_server(server: &EphemeralServer) {
112
126
  assert!(curr_seconds - 300 < resp_seconds && curr_seconds + 300 > resp_seconds);
113
127
  }
114
128
  }
115
-
116
- fn default_cached_download() -> EphemeralExe {
117
- EphemeralExe::CachedDownload {
118
- version: EphemeralExeVersion::Default {
119
- sdk_name: "sdk-rust".to_string(),
120
- sdk_version: "0.1.0".to_string(),
121
- },
122
- dest_dir: None,
123
- }
124
- }
125
-
126
- fn fixed_cached_download(version: &str) -> EphemeralExe {
127
- EphemeralExe::CachedDownload {
128
- version: EphemeralExeVersion::Fixed(version.to_string()),
129
- dest_dir: None,
130
- }
131
- }
132
-
133
- fn client_options(target: &str) -> ClientOptions {
134
- return ClientOptionsBuilder::default()
135
- .identity("integ_tester".to_string())
136
- .target_url(Url::try_from(&*format!("http://{}", target)).unwrap())
137
- .client_name("temporal-core".to_string())
138
- .client_version("0.1.0".to_string())
139
- .build()
140
- .unwrap();
141
- }
@@ -133,6 +133,7 @@ async fn can_paginate_long_history() {
133
133
  worker.run_until_done().await.unwrap();
134
134
  }
135
135
 
136
+ // TODO: Takes ages now, fix somehow
136
137
  #[tokio::test]
137
138
  async fn poll_of_nonexistent_namespace_is_fatal() {
138
139
  let mut starter = CoreWfStarter::new("whatever_yo");