@temporalio/core-bridge 1.9.2 → 1.10.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 (177) hide show
  1. package/Cargo.lock +754 -473
  2. package/Cargo.toml +3 -3
  3. package/lib/index.d.ts +33 -2
  4. package/lib/index.js.map +1 -1
  5. package/package.json +4 -4
  6. package/releases/aarch64-apple-darwin/index.node +0 -0
  7. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  8. package/releases/x86_64-apple-darwin/index.node +0 -0
  9. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  10. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  11. package/scripts/build.js +4 -3
  12. package/sdk-core/.cargo/config.toml +2 -4
  13. package/sdk-core/.github/workflows/heavy.yml +1 -1
  14. package/sdk-core/.github/workflows/per-pr.yml +6 -4
  15. package/sdk-core/Cargo.toml +10 -3
  16. package/sdk-core/README.md +4 -6
  17. package/sdk-core/client/Cargo.toml +13 -5
  18. package/sdk-core/client/src/lib.rs +123 -34
  19. package/sdk-core/client/src/metrics.rs +70 -18
  20. package/sdk-core/client/src/proxy.rs +85 -0
  21. package/sdk-core/client/src/raw.rs +67 -5
  22. package/sdk-core/client/src/worker_registry/mod.rs +5 -3
  23. package/sdk-core/client/src/workflow_handle/mod.rs +3 -1
  24. package/sdk-core/core/Cargo.toml +31 -37
  25. package/sdk-core/core/src/abstractions/take_cell.rs +3 -3
  26. package/sdk-core/core/src/abstractions.rs +176 -108
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +4 -13
  28. package/sdk-core/core/src/core_tests/determinism.rs +2 -1
  29. package/sdk-core/core/src/core_tests/local_activities.rs +3 -3
  30. package/sdk-core/core/src/core_tests/mod.rs +3 -3
  31. package/sdk-core/core/src/core_tests/queries.rs +42 -5
  32. package/sdk-core/core/src/core_tests/workers.rs +2 -3
  33. package/sdk-core/core/src/core_tests/workflow_tasks.rs +115 -15
  34. package/sdk-core/core/src/ephemeral_server/mod.rs +109 -136
  35. package/sdk-core/core/src/internal_flags.rs +8 -8
  36. package/sdk-core/core/src/lib.rs +16 -11
  37. package/sdk-core/core/src/pollers/mod.rs +11 -5
  38. package/sdk-core/core/src/pollers/poll_buffer.rs +48 -29
  39. package/sdk-core/core/src/protosext/mod.rs +32 -32
  40. package/sdk-core/core/src/protosext/protocol_messages.rs +14 -24
  41. package/sdk-core/core/src/retry_logic.rs +2 -2
  42. package/sdk-core/core/src/telemetry/log_export.rs +10 -9
  43. package/sdk-core/core/src/telemetry/metrics.rs +233 -330
  44. package/sdk-core/core/src/telemetry/mod.rs +11 -38
  45. package/sdk-core/core/src/telemetry/otel.rs +355 -0
  46. package/sdk-core/core/src/telemetry/prometheus_server.rs +36 -23
  47. package/sdk-core/core/src/test_help/mod.rs +80 -59
  48. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +6 -6
  49. package/sdk-core/core/src/worker/activities/local_activities.rs +46 -43
  50. package/sdk-core/core/src/worker/activities.rs +45 -46
  51. package/sdk-core/core/src/worker/client/mocks.rs +8 -7
  52. package/sdk-core/core/src/worker/client.rs +40 -39
  53. package/sdk-core/core/src/worker/mod.rs +72 -42
  54. package/sdk-core/core/src/worker/slot_provider.rs +28 -28
  55. package/sdk-core/core/src/worker/slot_supplier.rs +1 -0
  56. package/sdk-core/core/src/worker/tuner/fixed_size.rs +52 -0
  57. package/sdk-core/core/src/worker/tuner/resource_based.rs +561 -0
  58. package/sdk-core/core/src/worker/tuner.rs +122 -0
  59. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +6 -6
  60. package/sdk-core/core/src/worker/workflow/history_update.rs +27 -53
  61. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +4 -17
  62. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -10
  63. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +4 -11
  64. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +17 -35
  65. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +0 -8
  66. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +1 -5
  67. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +0 -5
  68. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +0 -5
  69. package/sdk-core/core/src/worker/workflow/machines/mod.rs +0 -14
  70. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +0 -5
  71. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +0 -5
  72. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -10
  73. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +3 -10
  74. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +12 -8
  75. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +0 -10
  76. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +6 -13
  77. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +27 -37
  78. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +3 -14
  79. package/sdk-core/core/src/worker/workflow/managed_run.rs +84 -54
  80. package/sdk-core/core/src/worker/workflow/mod.rs +63 -160
  81. package/sdk-core/core/src/worker/workflow/run_cache.rs +22 -13
  82. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +16 -3
  83. package/sdk-core/core/src/worker/workflow/wft_poller.rs +15 -12
  84. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +39 -78
  85. package/sdk-core/core-api/Cargo.toml +6 -5
  86. package/sdk-core/core-api/src/errors.rs +8 -0
  87. package/sdk-core/core-api/src/telemetry/metrics.rs +75 -4
  88. package/sdk-core/core-api/src/telemetry.rs +7 -1
  89. package/sdk-core/core-api/src/worker.rs +212 -56
  90. package/sdk-core/fsm/Cargo.toml +3 -0
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  92. package/sdk-core/sdk/Cargo.toml +5 -7
  93. package/sdk-core/sdk/src/app_data.rs +3 -3
  94. package/sdk-core/sdk/src/lib.rs +5 -3
  95. package/sdk-core/sdk/src/workflow_context/options.rs +1 -1
  96. package/sdk-core/sdk/src/workflow_context.rs +10 -9
  97. package/sdk-core/sdk/src/workflow_future.rs +1 -1
  98. package/sdk-core/sdk-core-protos/Cargo.toml +8 -6
  99. package/sdk-core/sdk-core-protos/build.rs +1 -10
  100. package/sdk-core/sdk-core-protos/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +3 -0
  101. package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/ci.yml +26 -0
  102. package/sdk-core/sdk-core-protos/protos/api_upstream/Makefile +42 -20
  103. package/sdk-core/sdk-core-protos/protos/api_upstream/README.md +2 -0
  104. package/sdk-core/sdk-core-protos/protos/api_upstream/api-linter.yaml +36 -26
  105. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.lock +2 -0
  106. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/struct.proto +95 -0
  107. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +9632 -0
  108. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +7337 -0
  109. package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/payload_description.txt +2 -0
  110. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +45 -11
  111. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +22 -4
  112. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/command_type.proto +2 -0
  113. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/common.proto +44 -0
  114. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +18 -3
  115. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +20 -0
  116. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +30 -0
  117. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +7 -8
  118. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/workflow.proto +23 -5
  119. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/errordetails/v1/message.proto +20 -0
  120. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +25 -0
  121. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +141 -15
  122. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/namespace/v1/message.proto +12 -0
  123. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +193 -0
  124. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +73 -6
  125. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +46 -4
  126. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +4 -0
  127. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +2 -2
  128. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +116 -0
  129. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +134 -0
  130. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +274 -29
  131. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +57 -1
  132. package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +10 -12
  133. package/sdk-core/sdk-core-protos/src/history_builder.rs +1 -1
  134. package/sdk-core/sdk-core-protos/src/lib.rs +54 -51
  135. package/sdk-core/sdk-core-protos/src/task_token.rs +11 -2
  136. package/sdk-core/test-utils/Cargo.toml +7 -4
  137. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  138. package/sdk-core/test-utils/src/lib.rs +44 -62
  139. package/sdk-core/tests/fuzzy_workflow.rs +5 -2
  140. package/sdk-core/tests/heavy_tests.rs +114 -17
  141. package/sdk-core/tests/integ_tests/activity_functions.rs +1 -1
  142. package/sdk-core/tests/integ_tests/client_tests.rs +2 -2
  143. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +38 -26
  144. package/sdk-core/tests/integ_tests/metrics_tests.rs +126 -17
  145. package/sdk-core/tests/integ_tests/polling_tests.rs +118 -2
  146. package/sdk-core/tests/integ_tests/update_tests.rs +3 -5
  147. package/sdk-core/tests/integ_tests/visibility_tests.rs +3 -3
  148. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +1 -1
  149. package/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +1 -1
  150. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -1
  151. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  152. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +3 -3
  153. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +5 -4
  154. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -2
  155. package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +6 -10
  156. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +9 -7
  157. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -1
  158. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +14 -9
  159. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
  160. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +6 -13
  161. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +9 -6
  162. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +5 -5
  163. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +1 -1
  164. package/sdk-core/tests/integ_tests/workflow_tests.rs +115 -11
  165. package/sdk-core/tests/main.rs +2 -2
  166. package/src/conversions.rs +57 -0
  167. package/src/lib.rs +1 -0
  168. package/src/runtime.rs +51 -35
  169. package/ts/index.ts +67 -3
  170. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +0 -117
  171. package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +0 -24
  172. package/sdk-core/sdk/src/payload_converter.rs +0 -11
  173. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/Dockerfile +0 -2
  174. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/docker-compose.yml +0 -15
  175. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/pipeline.yml +0 -10
  176. package/sdk-core/test-utils/src/wf_input_saver.rs +0 -50
  177. package/sdk-core/tests/wf_input_replay.rs +0 -32
@@ -1,37 +1,18 @@
1
- use crate::{
2
- abstractions::dbg_panic,
3
- telemetry::{
4
- default_resource, metric_temporality_to_selector, prometheus_server::PromServer,
5
- TelemetryInstance, TELEM_SERVICE_NAME,
6
- },
7
- };
8
- use opentelemetry::{
9
- self,
10
- metrics::{Meter, MeterProvider as MeterProviderT, Unit},
11
- KeyValue,
12
- };
13
- use opentelemetry_otlp::WithExportConfig;
14
- use opentelemetry_sdk::{
15
- metrics::{
16
- new_view,
17
- reader::{AggregationSelector, DefaultAggregationSelector},
18
- Aggregation, Instrument, InstrumentKind, MeterProvider, MeterProviderBuilder,
19
- PeriodicReader, View,
20
- },
21
- runtime, AttributeSet,
1
+ use crate::{abstractions::dbg_panic, telemetry::TelemetryInstance};
2
+
3
+ use std::{
4
+ fmt::{Debug, Display},
5
+ iter::Iterator,
6
+ sync::Arc,
7
+ time::Duration,
22
8
  };
23
- use parking_lot::RwLock;
24
- use std::{collections::HashMap, fmt::Debug, net::SocketAddr, sync::Arc, time::Duration};
25
- use temporal_sdk_core_api::telemetry::{
26
- metrics::{
27
- BufferAttributes, BufferInstrumentRef, CoreMeter, Counter, Gauge, Histogram,
28
- LazyBufferInstrument, MetricAttributes, MetricCallBufferer, MetricEvent, MetricKeyValue,
29
- MetricKind, MetricParameters, MetricUpdateVal, NewAttributes, NoOpCoreMeter,
30
- },
31
- OtelCollectorOptions, PrometheusExporterOptions,
9
+ use temporal_sdk_core_api::telemetry::metrics::{
10
+ BufferAttributes, BufferInstrumentRef, CoreMeter, Counter, Gauge, GaugeF64, Histogram,
11
+ HistogramDuration, HistogramF64, LazyBufferInstrument, MetricAttributes, MetricCallBufferer,
12
+ MetricEvent, MetricKeyValue, MetricKind, MetricParameters, MetricUpdateVal, NewAttributes,
13
+ NoOpCoreMeter,
32
14
  };
33
- use tokio::task::AbortHandle;
34
- use tonic::metadata::MetadataMap;
15
+ use temporal_sdk_core_protos::temporal::api::enums::v1::WorkflowTaskFailedCause;
35
16
 
36
17
  /// Used to track context associated with metrics, and record/update them
37
18
  ///
@@ -49,21 +30,22 @@ struct Instruments {
49
30
  wf_canceled_counter: Arc<dyn Counter>,
50
31
  wf_failed_counter: Arc<dyn Counter>,
51
32
  wf_cont_counter: Arc<dyn Counter>,
52
- wf_e2e_latency: Arc<dyn Histogram>,
33
+ wf_e2e_latency: Arc<dyn HistogramDuration>,
53
34
  wf_task_queue_poll_empty_counter: Arc<dyn Counter>,
54
35
  wf_task_queue_poll_succeed_counter: Arc<dyn Counter>,
55
36
  wf_task_execution_failure_counter: Arc<dyn Counter>,
56
- wf_task_sched_to_start_latency: Arc<dyn Histogram>,
57
- wf_task_replay_latency: Arc<dyn Histogram>,
58
- wf_task_execution_latency: Arc<dyn Histogram>,
37
+ wf_task_sched_to_start_latency: Arc<dyn HistogramDuration>,
38
+ wf_task_replay_latency: Arc<dyn HistogramDuration>,
39
+ wf_task_execution_latency: Arc<dyn HistogramDuration>,
59
40
  act_poll_no_task: Arc<dyn Counter>,
60
41
  act_task_received_counter: Arc<dyn Counter>,
61
42
  act_execution_failed: Arc<dyn Counter>,
62
- act_sched_to_start_latency: Arc<dyn Histogram>,
63
- act_exec_latency: Arc<dyn Histogram>,
43
+ act_sched_to_start_latency: Arc<dyn HistogramDuration>,
44
+ act_exec_latency: Arc<dyn HistogramDuration>,
64
45
  worker_registered: Arc<dyn Counter>,
65
46
  num_pollers: Arc<dyn Gauge>,
66
47
  task_slots_available: Arc<dyn Gauge>,
48
+ task_slots_used: Arc<dyn Gauge>,
67
49
  sticky_cache_hit: Arc<dyn Counter>,
68
50
  sticky_cache_miss: Arc<dyn Counter>,
69
51
  sticky_cache_size: Arc<dyn Gauge>,
@@ -156,30 +138,28 @@ impl MetricsContext {
156
138
 
157
139
  /// Record workflow total execution time in milliseconds
158
140
  pub(crate) fn wf_e2e_latency(&self, dur: Duration) {
159
- self.instruments
160
- .wf_e2e_latency
161
- .record(dur.as_millis() as u64, &self.kvs);
141
+ self.instruments.wf_e2e_latency.record(dur, &self.kvs);
162
142
  }
163
143
 
164
144
  /// Record workflow task schedule to start time in millis
165
145
  pub(crate) fn wf_task_sched_to_start_latency(&self, dur: Duration) {
166
146
  self.instruments
167
147
  .wf_task_sched_to_start_latency
168
- .record(dur.as_millis() as u64, &self.kvs);
148
+ .record(dur, &self.kvs);
169
149
  }
170
150
 
171
151
  /// Record workflow task execution time in milliseconds
172
152
  pub(crate) fn wf_task_latency(&self, dur: Duration) {
173
153
  self.instruments
174
154
  .wf_task_execution_latency
175
- .record(dur.as_millis() as u64, &self.kvs);
155
+ .record(dur, &self.kvs);
176
156
  }
177
157
 
178
158
  /// Record time it takes to catch up on replaying a WFT
179
159
  pub(crate) fn wf_task_replay_latency(&self, dur: Duration) {
180
160
  self.instruments
181
161
  .wf_task_replay_latency
182
- .record(dur.as_millis() as u64, &self.kvs);
162
+ .record(dur, &self.kvs);
183
163
  }
184
164
 
185
165
  /// An activity long poll timed out
@@ -201,15 +181,13 @@ impl MetricsContext {
201
181
  pub(crate) fn act_sched_to_start_latency(&self, dur: Duration) {
202
182
  self.instruments
203
183
  .act_sched_to_start_latency
204
- .record(dur.as_millis() as u64, &self.kvs);
184
+ .record(dur, &self.kvs);
205
185
  }
206
186
 
207
187
  /// Record time it took to complete activity execution, from the time core generated the
208
188
  /// activity task, to the time lang responded with a completion (failure or success).
209
189
  pub(crate) fn act_execution_latency(&self, dur: Duration) {
210
- self.instruments
211
- .act_exec_latency
212
- .record(dur.as_millis() as u64, &self.kvs);
190
+ self.instruments.act_exec_latency.record(dur, &self.kvs);
213
191
  }
214
192
 
215
193
  /// A worker was registered
@@ -224,6 +202,11 @@ impl MetricsContext {
224
202
  .record(num as u64, &self.kvs)
225
203
  }
226
204
 
205
+ /// Record current number of used task slots. Context should have worker type set.
206
+ pub(crate) fn task_slots_used(&self, num: u64) {
207
+ self.instruments.task_slots_used.record(num, &self.kvs)
208
+ }
209
+
227
210
  /// Record current number of pollers. Context should include poller type / task queue tag.
228
211
  pub(crate) fn record_num_pollers(&self, num: usize) {
229
212
  self.instruments.num_pollers.record(num as u64, &self.kvs);
@@ -273,9 +256,9 @@ impl Instruments {
273
256
  description: "Count of continued-as-new workflows".into(),
274
257
  unit: "".into(),
275
258
  }),
276
- wf_e2e_latency: meter.histogram(MetricParameters {
259
+ wf_e2e_latency: meter.histogram_duration(MetricParameters {
277
260
  name: WF_E2E_LATENCY_NAME.into(),
278
- unit: "ms".into(),
261
+ unit: "duration".into(),
279
262
  description: "Histogram of total workflow execution latencies".into(),
280
263
  }),
281
264
  wf_task_queue_poll_empty_counter: meter.counter(MetricParameters {
@@ -293,19 +276,19 @@ impl Instruments {
293
276
  description: "Count of workflow task execution failures".into(),
294
277
  unit: "".into(),
295
278
  }),
296
- wf_task_sched_to_start_latency: meter.histogram(MetricParameters {
279
+ wf_task_sched_to_start_latency: meter.histogram_duration(MetricParameters {
297
280
  name: WF_TASK_SCHED_TO_START_LATENCY_NAME.into(),
298
- unit: "ms".into(),
281
+ unit: "duration".into(),
299
282
  description: "Histogram of workflow task schedule-to-start latencies".into(),
300
283
  }),
301
- wf_task_replay_latency: meter.histogram(MetricParameters {
284
+ wf_task_replay_latency: meter.histogram_duration(MetricParameters {
302
285
  name: WF_TASK_REPLAY_LATENCY_NAME.into(),
303
- unit: "ms".into(),
286
+ unit: "duration".into(),
304
287
  description: "Histogram of workflow task replay latencies".into(),
305
288
  }),
306
- wf_task_execution_latency: meter.histogram(MetricParameters {
289
+ wf_task_execution_latency: meter.histogram_duration(MetricParameters {
307
290
  name: WF_TASK_EXECUTION_LATENCY_NAME.into(),
308
- unit: "ms".into(),
291
+ unit: "duration".into(),
309
292
  description: "Histogram of workflow task execution (not replay) latencies".into(),
310
293
  }),
311
294
  act_poll_no_task: meter.counter(MetricParameters {
@@ -323,14 +306,14 @@ impl Instruments {
323
306
  description: "Count of activity task execution failures".into(),
324
307
  unit: "".into(),
325
308
  }),
326
- act_sched_to_start_latency: meter.histogram(MetricParameters {
309
+ act_sched_to_start_latency: meter.histogram_duration(MetricParameters {
327
310
  name: ACT_SCHED_TO_START_LATENCY_NAME.into(),
328
- unit: "ms".into(),
311
+ unit: "duration".into(),
329
312
  description: "Histogram of activity schedule-to-start latencies".into(),
330
313
  }),
331
- act_exec_latency: meter.histogram(MetricParameters {
314
+ act_exec_latency: meter.histogram_duration(MetricParameters {
332
315
  name: ACT_EXEC_LATENCY_NAME.into(),
333
- unit: "ms".into(),
316
+ unit: "duration".into(),
334
317
  description: "Histogram of activity execution latencies".into(),
335
318
  }),
336
319
  // name kept as worker start for compat with old sdk / what users expect
@@ -349,6 +332,11 @@ impl Instruments {
349
332
  description: "Current number of available slots per task type".into(),
350
333
  unit: "".into(),
351
334
  }),
335
+ task_slots_used: meter.gauge(MetricParameters {
336
+ name: TASK_SLOTS_USED_NAME.into(),
337
+ description: "Current number of used slots per task type".into(),
338
+ unit: "".into(),
339
+ }),
352
340
  sticky_cache_hit: meter.counter(MetricParameters {
353
341
  name: "sticky_cache_hit".into(),
354
342
  description: "Count of times the workflow cache was used for a new workflow task"
@@ -383,6 +371,7 @@ const KEY_ACT_TYPE: &str = "activity_type";
383
371
  const KEY_POLLER_TYPE: &str = "poller_type";
384
372
  const KEY_WORKER_TYPE: &str = "worker_type";
385
373
  const KEY_EAGER: &str = "eager";
374
+ const KEY_TASK_FAILURE_TYPE: &str = "failure_reason";
386
375
 
387
376
  pub(crate) fn workflow_poller() -> MetricKeyValue {
388
377
  MetricKeyValue::new(KEY_POLLER_TYPE, "workflow_task")
@@ -414,217 +403,115 @@ pub(crate) fn local_activity_worker_type() -> MetricKeyValue {
414
403
  pub(crate) fn eager(is_eager: bool) -> MetricKeyValue {
415
404
  MetricKeyValue::new(KEY_EAGER, is_eager)
416
405
  }
417
-
418
- const WF_E2E_LATENCY_NAME: &str = "workflow_endtoend_latency";
419
- const WF_TASK_SCHED_TO_START_LATENCY_NAME: &str = "workflow_task_schedule_to_start_latency";
420
- const WF_TASK_REPLAY_LATENCY_NAME: &str = "workflow_task_replay_latency";
421
- const WF_TASK_EXECUTION_LATENCY_NAME: &str = "workflow_task_execution_latency";
422
- const ACT_SCHED_TO_START_LATENCY_NAME: &str = "activity_schedule_to_start_latency";
423
- const ACT_EXEC_LATENCY_NAME: &str = "activity_execution_latency";
424
- const NUM_POLLERS_NAME: &str = "num_pollers";
425
- const TASK_SLOTS_AVAILABLE_NAME: &str = "worker_task_slots_available";
426
- const STICKY_CACHE_SIZE_NAME: &str = "sticky_cache_size";
427
-
428
- /// Artisanal, handcrafted latency buckets for workflow e2e latency which should expose a useful
429
- /// set of buckets for < 1 day runtime workflows. Beyond that, this metric probably isn't very
430
- /// helpful
431
- static WF_LATENCY_MS_BUCKETS: &[f64] = &[
432
- 100.,
433
- 500.,
434
- 1000.,
435
- 1500.,
436
- 2000.,
437
- 5000.,
438
- 10_000.,
439
- 30_000.,
440
- 60_000.,
441
- 120_000.,
442
- 300_000.,
443
- 600_000.,
444
- 1_800_000., // 30 min
445
- 3_600_000., // 1 hr
446
- 30_600_000., // 10 hrs
447
- 8.64e7, // 24 hrs
448
- ];
449
-
450
- /// Task latencies are expected to be fast, no longer than a second which was generally the deadlock
451
- /// timeout in old SDKs. Here it's a bit different since a WFT may represent multiple activations.
452
- static WF_TASK_MS_BUCKETS: &[f64] = &[1., 10., 20., 50., 100., 200., 500., 1000.];
453
-
454
- /// Activity are generally expected to take at least a little time, and sometimes quite a while,
455
- /// since they're doing side-effecty things, etc.
456
- static ACT_EXE_MS_BUCKETS: &[f64] = &[50., 100., 500., 1000., 5000., 10_000., 60_000.];
457
-
458
- /// Schedule-to-start latency buckets for both WFT and AT
459
- static TASK_SCHED_TO_START_MS_BUCKETS: &[f64] =
460
- &[100., 500., 1000., 5000., 10_000., 100_000., 1_000_000.];
461
-
462
- /// Default buckets. Should never really be used as they will be meaningless for many things, but
463
- /// broadly it's trying to represent latencies in millis.
464
- pub(super) static DEFAULT_MS_BUCKETS: &[f64] = &[50., 100., 500., 1000., 2500., 10_000.];
465
-
466
- /// Returns the default histogram buckets that lang should use for a given metric name if they
467
- /// have not been overridden by the user.
468
- ///
469
- /// The name must *not* be prefixed with `temporal_`
470
- pub fn default_buckets_for(histo_name: &str) -> &'static [f64] {
471
- match histo_name {
472
- WF_E2E_LATENCY_NAME => WF_LATENCY_MS_BUCKETS,
473
- WF_TASK_EXECUTION_LATENCY_NAME | WF_TASK_REPLAY_LATENCY_NAME => WF_TASK_MS_BUCKETS,
474
- WF_TASK_SCHED_TO_START_LATENCY_NAME | ACT_SCHED_TO_START_LATENCY_NAME => {
475
- TASK_SCHED_TO_START_MS_BUCKETS
476
- }
477
- ACT_EXEC_LATENCY_NAME => ACT_EXE_MS_BUCKETS,
478
- _ => DEFAULT_MS_BUCKETS,
479
- }
480
- }
481
-
482
- /// Chooses appropriate aggregators for our metrics
483
- #[derive(Debug, Clone, Default)]
484
- pub struct SDKAggSelector {
485
- default: DefaultAggregationSelector,
406
+ pub(crate) enum FailureReason {
407
+ Nondeterminism,
408
+ Workflow,
486
409
  }
487
- impl AggregationSelector for SDKAggSelector {
488
- fn aggregation(&self, kind: InstrumentKind) -> Aggregation {
489
- match kind {
490
- InstrumentKind::Histogram => Aggregation::ExplicitBucketHistogram {
491
- boundaries: DEFAULT_MS_BUCKETS.to_vec(),
492
- record_min_max: true,
493
- },
494
- _ => self.default.aggregation(kind),
495
- }
410
+ impl Display for FailureReason {
411
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
412
+ let str = match self {
413
+ FailureReason::Nondeterminism => "NonDeterminismError",
414
+ FailureReason::Workflow => "WorkflowError",
415
+ };
416
+ write!(f, "{}", str)
496
417
  }
497
418
  }
498
-
499
- fn histo_view(
500
- metric_name: &'static str,
501
- buckets: &[f64],
502
- ) -> opentelemetry::metrics::Result<Box<dyn View>> {
503
- new_view(
504
- Instrument::new().name(format!("*{metric_name}")),
505
- opentelemetry_sdk::metrics::Stream::new().aggregation(
506
- Aggregation::ExplicitBucketHistogram {
507
- boundaries: buckets.to_vec(),
508
- record_min_max: true,
509
- },
510
- ),
511
- )
512
- }
513
-
514
- pub(super) fn augment_meter_provider_with_defaults(
515
- mpb: MeterProviderBuilder,
516
- global_tags: &HashMap<String, String>,
517
- ) -> opentelemetry::metrics::Result<MeterProviderBuilder> {
518
- // Some histograms are actually gauges, but we have to use histograms otherwise they forget
519
- // their value between collections since we don't use callbacks.
520
- Ok(mpb
521
- .with_view(histo_view(WF_E2E_LATENCY_NAME, WF_LATENCY_MS_BUCKETS)?)
522
- .with_view(histo_view(
523
- WF_TASK_EXECUTION_LATENCY_NAME,
524
- WF_TASK_MS_BUCKETS,
525
- )?)
526
- .with_view(histo_view(WF_TASK_REPLAY_LATENCY_NAME, WF_TASK_MS_BUCKETS)?)
527
- .with_view(histo_view(
528
- WF_TASK_SCHED_TO_START_LATENCY_NAME,
529
- TASK_SCHED_TO_START_MS_BUCKETS,
530
- )?)
531
- .with_view(histo_view(
532
- ACT_SCHED_TO_START_LATENCY_NAME,
533
- TASK_SCHED_TO_START_MS_BUCKETS,
534
- )?)
535
- .with_view(histo_view(ACT_EXEC_LATENCY_NAME, ACT_EXE_MS_BUCKETS)?)
536
- .with_resource(default_resource(global_tags)))
537
- }
538
-
539
- /// OTel has no built-in synchronous Gauge. Histograms used to be able to serve that purpose, but
540
- /// they broke that. Lovely. So, we need to implement one by hand.
541
- pub(crate) struct MemoryGaugeU64 {
542
- labels_to_values: Arc<RwLock<HashMap<AttributeSet, u64>>>,
543
- }
544
-
545
- impl MemoryGaugeU64 {
546
- fn new(params: MetricParameters, meter: &Meter) -> Self {
547
- let gauge = meter
548
- .u64_observable_gauge(params.name)
549
- .with_unit(Unit::new(params.unit))
550
- .with_description(params.description)
551
- .init();
552
- let map = Arc::new(RwLock::new(HashMap::<AttributeSet, u64>::new()));
553
- let map_c = map.clone();
554
- meter
555
- .register_callback(&[gauge.as_any()], move |o| {
556
- // This whole thing is... extra stupid.
557
- // See https://github.com/open-telemetry/opentelemetry-rust/issues/1181
558
- // The performance is likely bad here, but, given this is only called when metrics
559
- // are exported it should be livable for now.
560
- let map_rlock = map_c.read();
561
- for (kvs, val) in map_rlock.iter() {
562
- let kvs: Vec<_> = kvs
563
- .iter()
564
- .map(|(k, v)| KeyValue::new(k.clone(), v.clone()))
565
- .collect();
566
- o.observe_u64(&gauge, *val, kvs.as_slice())
567
- }
568
- })
569
- .expect("instrument must exist we just created it");
570
- MemoryGaugeU64 {
571
- labels_to_values: map,
419
+ impl From<WorkflowTaskFailedCause> for FailureReason {
420
+ fn from(v: WorkflowTaskFailedCause) -> Self {
421
+ match v {
422
+ WorkflowTaskFailedCause::NonDeterministicError => FailureReason::Nondeterminism,
423
+ _ => FailureReason::Workflow,
572
424
  }
573
425
  }
574
- fn record(&self, val: u64, kvs: &[KeyValue]) {
575
- self.labels_to_values
576
- .write()
577
- .insert(AttributeSet::from(kvs), val);
578
- }
579
- }
580
-
581
- /// Create an OTel meter that can be used as a [CoreMeter] to export metrics over OTLP.
582
- pub fn build_otlp_metric_exporter(
583
- opts: OtelCollectorOptions,
584
- ) -> Result<CoreOtelMeter, anyhow::Error> {
585
- let exporter = opentelemetry_otlp::TonicExporterBuilder::default()
586
- .with_endpoint(opts.url.to_string())
587
- .with_metadata(MetadataMap::from_headers((&opts.headers).try_into()?))
588
- .build_metrics_exporter(
589
- Box::<SDKAggSelector>::default(),
590
- Box::new(metric_temporality_to_selector(opts.metric_temporality)),
591
- )?;
592
- let reader = PeriodicReader::builder(exporter, runtime::Tokio)
593
- .with_interval(opts.metric_periodicity)
594
- .build();
595
- let mp = augment_meter_provider_with_defaults(
596
- MeterProvider::builder().with_reader(reader),
597
- &opts.global_tags,
598
- )?
599
- .build();
600
- Ok::<_, anyhow::Error>(CoreOtelMeter(mp.meter(TELEM_SERVICE_NAME)))
601
426
  }
602
-
603
- pub struct StartedPromServer {
604
- pub meter: Arc<CoreOtelMeter>,
605
- pub bound_addr: SocketAddr,
606
- pub abort_handle: AbortHandle,
427
+ pub(crate) fn failure_reason(reason: FailureReason) -> MetricKeyValue {
428
+ MetricKeyValue::new(KEY_TASK_FAILURE_TYPE, reason.to_string())
429
+ }
430
+
431
+ pub(super) const WF_E2E_LATENCY_NAME: &str = "workflow_endtoend_latency";
432
+ pub(super) const WF_TASK_SCHED_TO_START_LATENCY_NAME: &str =
433
+ "workflow_task_schedule_to_start_latency";
434
+ pub(super) const WF_TASK_REPLAY_LATENCY_NAME: &str = "workflow_task_replay_latency";
435
+ pub(super) const WF_TASK_EXECUTION_LATENCY_NAME: &str = "workflow_task_execution_latency";
436
+ pub(super) const ACT_SCHED_TO_START_LATENCY_NAME: &str = "activity_schedule_to_start_latency";
437
+ pub(super) const ACT_EXEC_LATENCY_NAME: &str = "activity_execution_latency";
438
+ pub(super) const NUM_POLLERS_NAME: &str = "num_pollers";
439
+ pub(super) const TASK_SLOTS_AVAILABLE_NAME: &str = "worker_task_slots_available";
440
+ pub(super) const TASK_SLOTS_USED_NAME: &str = "worker_task_slots_used";
441
+ pub(super) const STICKY_CACHE_SIZE_NAME: &str = "sticky_cache_size";
442
+
443
+ /// Helps define buckets once in terms of millis, but also generates a seconds version
444
+ macro_rules! define_latency_buckets {
445
+ ($(($metric_name:pat, $name:ident, $sec_name:ident, [$($bucket:expr),*])),*) => {
446
+ $(
447
+ pub(super) static $name: &[f64] = &[$($bucket,)*];
448
+ pub(super) static $sec_name: &[f64] = &[$( $bucket / 1000.0, )*];
449
+ )*
450
+
451
+ /// Returns the default histogram buckets that lang should use for a given metric name if
452
+ /// they have not been overridden by the user. If `use_seconds` is true, returns buckets
453
+ /// in terms of seconds rather than milliseconds.
454
+ ///
455
+ /// The name must *not* be prefixed with `temporal_`
456
+ pub fn default_buckets_for(histo_name: &str, use_seconds: bool) -> &'static [f64] {
457
+ match histo_name {
458
+ $(
459
+ $metric_name => { if use_seconds { &$sec_name } else { &$name } },
460
+ )*
461
+ }
462
+ }
463
+ };
607
464
  }
608
465
 
609
- /// Builds and runs a prometheus endpoint which can be scraped by prom instances for metrics export.
610
- /// Returns the meter that can be used as a [CoreMeter].
611
- pub fn start_prometheus_metric_exporter(
612
- opts: PrometheusExporterOptions,
613
- ) -> Result<StartedPromServer, anyhow::Error> {
614
- let (srv, exporter) = PromServer::new(&opts, SDKAggSelector::default())?;
615
- let meter_provider = augment_meter_provider_with_defaults(
616
- MeterProvider::builder().with_reader(exporter),
617
- &opts.global_tags,
618
- )?
619
- .build();
620
- let bound_addr = srv.bound_addr();
621
- let handle = tokio::spawn(async move { srv.run().await });
622
- Ok(StartedPromServer {
623
- meter: Arc::new(CoreOtelMeter(meter_provider.meter(TELEM_SERVICE_NAME))),
624
- bound_addr,
625
- abort_handle: handle.abort_handle(),
626
- })
627
- }
466
+ define_latency_buckets!(
467
+ (
468
+ WF_E2E_LATENCY_NAME,
469
+ WF_LATENCY_MS_BUCKETS,
470
+ WF_LATENCY_S_BUCKETS,
471
+ [
472
+ 100.,
473
+ 500.,
474
+ 1000.,
475
+ 1500.,
476
+ 2000.,
477
+ 5000.,
478
+ 10_000.,
479
+ 30_000.,
480
+ 60_000.,
481
+ 120_000.,
482
+ 300_000.,
483
+ 600_000.,
484
+ 1_800_000., // 30 min
485
+ 3_600_000., // 1 hr
486
+ 30_600_000., // 10 hrs
487
+ 8.64e7 // 24 hrs
488
+ ]
489
+ ),
490
+ (
491
+ WF_TASK_EXECUTION_LATENCY_NAME | WF_TASK_REPLAY_LATENCY_NAME,
492
+ WF_TASK_MS_BUCKETS,
493
+ WF_TASK_S_BUCKETS,
494
+ [1., 10., 20., 50., 100., 200., 500., 1000.]
495
+ ),
496
+ (
497
+ ACT_EXEC_LATENCY_NAME,
498
+ ACT_EXE_MS_BUCKETS,
499
+ ACT_EXE_S_BUCKETS,
500
+ [50., 100., 500., 1000., 5000., 10_000., 60_000.]
501
+ ),
502
+ (
503
+ WF_TASK_SCHED_TO_START_LATENCY_NAME | ACT_SCHED_TO_START_LATENCY_NAME,
504
+ TASK_SCHED_TO_START_MS_BUCKETS,
505
+ TASK_SCHED_TO_START_S_BUCKETS,
506
+ [100., 500., 1000., 5000., 10_000., 100_000., 1_000_000.]
507
+ ),
508
+ (
509
+ _,
510
+ DEFAULT_MS_BUCKETS,
511
+ DEFAULT_S_BUCKETS,
512
+ [50., 100., 500., 1000., 2500., 10_000.]
513
+ )
514
+ );
628
515
 
629
516
  /// Buffers [MetricEvent]s for periodic consumption by lang
630
517
  #[derive(Debug)]
@@ -668,7 +555,6 @@ where
668
555
  populate_into: hole.clone(),
669
556
  });
670
557
  BufferInstrument {
671
- kind,
672
558
  instrument_ref: hole,
673
559
  tx: self.calls_tx.clone(),
674
560
  }
@@ -716,9 +602,21 @@ where
716
602
  Arc::new(self.new_instrument(params, MetricKind::Histogram))
717
603
  }
718
604
 
605
+ fn histogram_f64(&self, params: MetricParameters) -> Arc<dyn HistogramF64> {
606
+ Arc::new(self.new_instrument(params, MetricKind::HistogramF64))
607
+ }
608
+
609
+ fn histogram_duration(&self, params: MetricParameters) -> Arc<dyn HistogramDuration> {
610
+ Arc::new(self.new_instrument(params, MetricKind::HistogramDuration))
611
+ }
612
+
719
613
  fn gauge(&self, params: MetricParameters) -> Arc<dyn Gauge> {
720
614
  Arc::new(self.new_instrument(params, MetricKind::Gauge))
721
615
  }
616
+
617
+ fn gauge_f64(&self, params: MetricParameters) -> Arc<dyn GaugeF64> {
618
+ Arc::new(self.new_instrument(params, MetricKind::GaugeF64))
619
+ }
722
620
  }
723
621
  impl<I> MetricCallBufferer<I> for MetricsCallBuffer<I>
724
622
  where
@@ -730,7 +628,6 @@ where
730
628
  }
731
629
 
732
630
  struct BufferInstrument<I: BufferInstrumentRef> {
733
- kind: MetricKind,
734
631
  instrument_ref: LazyBufferInstrument<I>,
735
632
  tx: LogErrOnFullSender<MetricEvent<I>>,
736
633
  }
@@ -738,17 +635,14 @@ impl<I> BufferInstrument<I>
738
635
  where
739
636
  I: Clone + BufferInstrumentRef,
740
637
  {
741
- fn send(&self, value: u64, attributes: &MetricAttributes) {
638
+ fn send(&self, value: MetricUpdateVal, attributes: &MetricAttributes) {
742
639
  let attributes = match attributes {
743
640
  MetricAttributes::Buffer(l) => l.clone(),
744
641
  _ => panic!("MetricsCallBuffer only works with MetricAttributes::Lang"),
745
642
  };
746
643
  self.tx.send(MetricEvent::Update {
747
644
  instrument: self.instrument_ref.clone(),
748
- update: match self.kind {
749
- MetricKind::Counter => MetricUpdateVal::Delta(value),
750
- MetricKind::Gauge | MetricKind::Histogram => MetricUpdateVal::Value(value),
751
- },
645
+ update: value,
752
646
  attributes: attributes.clone(),
753
647
  });
754
648
  }
@@ -758,7 +652,7 @@ where
758
652
  I: BufferInstrumentRef + Send + Sync + Clone,
759
653
  {
760
654
  fn add(&self, value: u64, attributes: &MetricAttributes) {
761
- self.send(value, attributes)
655
+ self.send(MetricUpdateVal::Delta(value), attributes)
762
656
  }
763
657
  }
764
658
  impl<I> Gauge for BufferInstrument<I>
@@ -766,7 +660,15 @@ where
766
660
  I: BufferInstrumentRef + Send + Sync + Clone,
767
661
  {
768
662
  fn record(&self, value: u64, attributes: &MetricAttributes) {
769
- self.send(value, attributes)
663
+ self.send(MetricUpdateVal::Value(value), attributes)
664
+ }
665
+ }
666
+ impl<I> GaugeF64 for BufferInstrument<I>
667
+ where
668
+ I: BufferInstrumentRef + Send + Sync + Clone,
669
+ {
670
+ fn record(&self, value: f64, attributes: &MetricAttributes) {
671
+ self.send(MetricUpdateVal::ValueF64(value), attributes)
770
672
  }
771
673
  }
772
674
  impl<I> Histogram for BufferInstrument<I>
@@ -774,65 +676,23 @@ where
774
676
  I: BufferInstrumentRef + Send + Sync + Clone,
775
677
  {
776
678
  fn record(&self, value: u64, attributes: &MetricAttributes) {
777
- self.send(value, attributes)
679
+ self.send(MetricUpdateVal::Value(value), attributes)
778
680
  }
779
681
  }
780
-
781
- #[derive(Debug)]
782
- pub struct CoreOtelMeter(Meter);
783
- impl CoreMeter for CoreOtelMeter {
784
- fn new_attributes(&self, attribs: NewAttributes) -> MetricAttributes {
785
- MetricAttributes::OTel {
786
- kvs: Arc::new(attribs.attributes.into_iter().map(KeyValue::from).collect()),
787
- }
788
- }
789
-
790
- fn extend_attributes(
791
- &self,
792
- existing: MetricAttributes,
793
- attribs: NewAttributes,
794
- ) -> MetricAttributes {
795
- if let MetricAttributes::OTel { mut kvs } = existing {
796
- Arc::make_mut(&mut kvs).extend(attribs.attributes.into_iter().map(Into::into));
797
- MetricAttributes::OTel { kvs }
798
- } else {
799
- dbg_panic!("Must use OTel attributes with an OTel metric implementation");
800
- existing
801
- }
802
- }
803
-
804
- fn counter(&self, params: MetricParameters) -> Arc<dyn Counter> {
805
- Arc::new(
806
- self.0
807
- .u64_counter(params.name)
808
- .with_unit(Unit::new(params.unit))
809
- .with_description(params.description)
810
- .init(),
811
- )
812
- }
813
-
814
- fn histogram(&self, params: MetricParameters) -> Arc<dyn Histogram> {
815
- Arc::new(
816
- self.0
817
- .u64_histogram(params.name)
818
- .with_unit(Unit::new(params.unit))
819
- .with_description(params.description)
820
- .init(),
821
- )
822
- }
823
-
824
- fn gauge(&self, params: MetricParameters) -> Arc<dyn Gauge> {
825
- Arc::new(MemoryGaugeU64::new(params, &self.0))
682
+ impl<I> HistogramF64 for BufferInstrument<I>
683
+ where
684
+ I: BufferInstrumentRef + Send + Sync + Clone,
685
+ {
686
+ fn record(&self, value: f64, attributes: &MetricAttributes) {
687
+ self.send(MetricUpdateVal::ValueF64(value), attributes)
826
688
  }
827
689
  }
828
-
829
- impl Gauge for MemoryGaugeU64 {
830
- fn record(&self, value: u64, attributes: &MetricAttributes) {
831
- if let MetricAttributes::OTel { kvs } = attributes {
832
- self.record(value, kvs);
833
- } else {
834
- dbg_panic!("Must use OTel attributes with an OTel metric implementation");
835
- }
690
+ impl<I> HistogramDuration for BufferInstrument<I>
691
+ where
692
+ I: BufferInstrumentRef + Send + Sync + Clone,
693
+ {
694
+ fn record(&self, value: Duration, attributes: &MetricAttributes) {
695
+ self.send(MetricUpdateVal::Duration(value), attributes)
836
696
  }
837
697
  }
838
698
 
@@ -864,10 +724,25 @@ impl<CM: CoreMeter> CoreMeter for PrefixedMetricsMeter<CM> {
864
724
  self.meter.histogram(params)
865
725
  }
866
726
 
727
+ fn histogram_f64(&self, mut params: MetricParameters) -> Arc<dyn HistogramF64> {
728
+ params.name = (self.prefix.clone() + &*params.name).into();
729
+ self.meter.histogram_f64(params)
730
+ }
731
+
732
+ fn histogram_duration(&self, mut params: MetricParameters) -> Arc<dyn HistogramDuration> {
733
+ params.name = (self.prefix.clone() + &*params.name).into();
734
+ self.meter.histogram_duration(params)
735
+ }
736
+
867
737
  fn gauge(&self, mut params: MetricParameters) -> Arc<dyn Gauge> {
868
738
  params.name = (self.prefix.clone() + &*params.name).into();
869
739
  self.meter.gauge(params)
870
740
  }
741
+
742
+ fn gauge_f64(&self, mut params: MetricParameters) -> Arc<dyn GaugeF64> {
743
+ params.name = (self.prefix.clone() + &*params.name).into();
744
+ self.meter.gauge_f64(params)
745
+ }
871
746
  }
872
747
 
873
748
  #[cfg(test)]
@@ -932,7 +807,7 @@ mod tests {
932
807
  a1.set(Arc::new(DummyCustomAttrs(1))).unwrap();
933
808
  // Verify all metrics are created. This number will need to get updated any time a metric
934
809
  // is added.
935
- let num_metrics = 23;
810
+ let num_metrics = 24;
936
811
  #[allow(clippy::needless_range_loop)] // Sorry clippy, this reads easier.
937
812
  for metric_num in 1..=num_metrics {
938
813
  let hole = assert_matches!(&events[metric_num],
@@ -970,9 +845,10 @@ mod tests {
970
845
  MetricEvent::Update {
971
846
  instrument,
972
847
  attributes,
973
- update: MetricUpdateVal::Value(1000) // milliseconds
848
+ update: MetricUpdateVal::Duration(d)
974
849
  }
975
850
  if DummyCustomAttrs::as_id(attributes) == 2 && instrument.get().0 == 11
851
+ && d == &Duration::from_secs(1)
976
852
  );
977
853
  }
978
854
 
@@ -994,6 +870,11 @@ mod tests {
994
870
  description: "a counter".into(),
995
871
  unit: "bleezles".into(),
996
872
  });
873
+ let histo_dur = call_buffer.histogram_duration(MetricParameters {
874
+ name: "histo_dur".into(),
875
+ description: "a duration histogram".into(),
876
+ unit: "seconds".into(),
877
+ });
997
878
  let attrs_1 = call_buffer.new_attributes(NewAttributes {
998
879
  attributes: vec![MetricKeyValue::new("hi", "yo")],
999
880
  });
@@ -1003,6 +884,7 @@ mod tests {
1003
884
  ctr.add(1, &attrs_1);
1004
885
  histo.record(2, &attrs_1);
1005
886
  gauge.record(3, &attrs_2);
887
+ histo_dur.record(Duration::from_secs_f64(1.2), &attrs_1);
1006
888
 
1007
889
  let mut calls = call_buffer.retrieve();
1008
890
  calls.reverse();
@@ -1039,6 +921,17 @@ mod tests {
1039
921
  => populate_into
1040
922
  );
1041
923
  gauge_3.set(Arc::new(DummyInstrumentRef(3))).unwrap();
924
+ let hist_4 = assert_matches!(
925
+ calls.pop(),
926
+ Some(MetricEvent::Create {
927
+ params,
928
+ populate_into,
929
+ kind: MetricKind::HistogramDuration
930
+ })
931
+ if params.name == "histo_dur"
932
+ => populate_into
933
+ );
934
+ hist_4.set(Arc::new(DummyInstrumentRef(4))).unwrap();
1042
935
  let a1 = assert_matches!(
1043
936
  calls.pop(),
1044
937
  Some(MetricEvent::CreateAttributes {
@@ -1086,7 +979,17 @@ mod tests {
1086
979
  attributes,
1087
980
  update: MetricUpdateVal::Value(3)
1088
981
  })
1089
- if DummyCustomAttrs::as_id(&attributes) == 2&& instrument.get().0 == 3
982
+ if DummyCustomAttrs::as_id(&attributes) == 2 && instrument.get().0 == 3
983
+ );
984
+ assert_matches!(
985
+ calls.pop(),
986
+ Some(MetricEvent::Update{
987
+ instrument,
988
+ attributes,
989
+ update: MetricUpdateVal::Duration(d)
990
+ })
991
+ if DummyCustomAttrs::as_id(&attributes) == 1 && instrument.get().0 == 4
992
+ && d == Duration::from_secs_f64(1.2)
1090
993
  );
1091
994
  }
1092
995
  }