@temporalio/core-bridge 1.14.1 → 1.15.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.
- package/Cargo.lock +648 -606
- package/bridge-macros/src/derive_tryintojs.rs +40 -0
- package/lib/native.d.ts +23 -2
- package/package.json +12 -13
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.cargo/multi-worker-manual-test +0 -0
- package/sdk-core/AGENTS.md +2 -2
- package/sdk-core/Cargo.toml +1 -1
- package/sdk-core/README.md +5 -5
- package/sdk-core/crates/client/src/raw.rs +90 -0
- package/sdk-core/crates/client/src/worker/mod.rs +103 -28
- package/sdk-core/crates/common/Cargo.toml +1 -1
- package/sdk-core/crates/common/protos/api_upstream/.github/workflows/create-release.yml +0 -5
- package/sdk-core/crates/common/protos/api_upstream/README.md +8 -0
- package/sdk-core/crates/common/protos/api_upstream/buf.yaml +3 -0
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +2738 -2452
- package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +1657 -124
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/activity/v1/message.proto +155 -3
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/common/v1/message.proto +8 -1
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +26 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/activity.proto +81 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +15 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +62 -15
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/errordetails/v1/message.proto +8 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +107 -17
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +15 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +4 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -0
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/worker/v1/message.proto +4 -7
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflow/v1/message.proto +80 -22
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +285 -19
- package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +154 -10
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/core_interface.proto +15 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +5 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
- package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +17 -0
- package/sdk-core/crates/common/src/lib.rs +3 -3
- package/sdk-core/crates/common/src/protos/canned_histories.rs +16 -0
- package/sdk-core/crates/common/src/protos/mod.rs +12 -0
- package/sdk-core/crates/common/src/telemetry/metrics.rs +6 -4
- package/sdk-core/crates/common/src/telemetry.rs +14 -15
- package/sdk-core/crates/common/src/worker.rs +66 -99
- package/sdk-core/crates/common/tests/worker_task_types_test.rs +9 -9
- package/sdk-core/crates/sdk/src/lib.rs +10 -8
- package/sdk-core/crates/sdk/src/workflow_context/options.rs +19 -0
- package/sdk-core/crates/sdk-core/Cargo.toml +2 -1
- package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +4 -19
- package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +9 -6
- package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +166 -13
- package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +42 -33
- package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +6 -9
- package/sdk-core/crates/sdk-core/src/lib.rs +20 -13
- package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +301 -21
- package/sdk-core/crates/sdk-core/src/telemetry/log_export.rs +7 -10
- package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +4 -2
- package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +2 -3
- package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +30 -8
- package/sdk-core/crates/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +1 -0
- package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +3 -1
- package/sdk-core/crates/sdk-core/src/worker/client.rs +2 -6
- package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +4 -4
- package/sdk-core/crates/sdk-core/src/worker/mod.rs +92 -53
- package/sdk-core/crates/sdk-core/src/worker/nexus.rs +5 -0
- package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +12 -14
- package/sdk-core/crates/sdk-core/src/worker/tuner.rs +36 -36
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/patch_state_machine.rs +5 -8
- package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +12 -1
- package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +6 -23
- package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +46 -3
- package/sdk-core/crates/sdk-core/tests/common/mod.rs +45 -45
- package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +7 -10
- package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +3 -5
- package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +34 -42
- package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +21 -26
- package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +1 -0
- package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +147 -72
- package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +27 -48
- package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +5 -15
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +61 -66
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +16 -14
- package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +15 -21
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +16 -19
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -12
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +14 -9
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +2 -6
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +4 -8
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +11 -13
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +11 -27
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +3 -5
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +4 -12
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +7 -13
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +4 -12
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +1 -3
- package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +16 -30
- package/sdk-core/crates/sdk-core/tests/main.rs +6 -2
- package/sdk-core/crates/sdk-core/tests/manual_tests.rs +40 -49
- package/sdk-core/crates/sdk-core/tests/runner.rs +4 -6
- package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +28 -13
- package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +1 -0
- package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +24 -13
- package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +103 -19
- package/sdk-core/crates/sdk-core-c-bridge/src/lib.rs +89 -5
- package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +1 -2
- package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +59 -66
- package/sdk-core/crates/sdk-core-c-bridge/src/testing.rs +10 -10
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +46 -11
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +103 -7
- package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +6 -48
- package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +13 -17
- package/sdk-core/docker-cgroup-tests.sh +0 -0
- package/sdk-core/etc/cargo-tokio-console.sh +0 -0
- package/sdk-core/etc/integ-with-otel.sh +0 -0
- package/sdk-core/etc/regen-depgraph.sh +0 -0
- package/src/client.rs +30 -0
- package/src/helpers/try_into_js.rs +88 -2
- package/src/metrics.rs +272 -22
- package/src/runtime.rs +91 -41
- package/src/testing.rs +9 -16
- package/src/worker.rs +76 -55
- package/ts/native.ts +38 -2
- package/LICENSE +0 -21
- package/sdk-core/crates/macros/LICENSE.txt +0 -21
package/src/metrics.rs
CHANGED
|
@@ -1,28 +1,39 @@
|
|
|
1
|
+
use std::any::Any;
|
|
1
2
|
use std::collections::HashMap;
|
|
3
|
+
use std::sync::Arc;
|
|
2
4
|
|
|
3
|
-
use anyhow::Context as _;
|
|
4
5
|
use neon::prelude::*;
|
|
5
6
|
use serde::Deserialize;
|
|
6
7
|
|
|
7
8
|
use temporalio_common::telemetry::metrics::{
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
BufferInstrumentRef as CoreBufferInstrumentRef, CoreMeter, Counter as CoreCounter,
|
|
10
|
+
CustomMetricAttributes, Gauge as CoreGauge, Histogram as CoreHistogram, MetricCallBufferer,
|
|
11
|
+
MetricEvent as CoreMetricEvent, MetricKind as CoreMetricKind,
|
|
12
|
+
MetricParameters as CoreMetricParameters, NewAttributes, TemporalMeter,
|
|
10
13
|
};
|
|
11
14
|
use temporalio_common::telemetry::metrics::{
|
|
12
15
|
GaugeF64 as CoreGaugeF64, HistogramF64 as CoreHistogramF64,
|
|
13
16
|
};
|
|
14
|
-
use temporalio_common::telemetry::
|
|
15
|
-
|
|
17
|
+
use temporalio_common::telemetry::{
|
|
18
|
+
metrics,
|
|
19
|
+
metrics::{MetricKeyValue as CoreMetricKeyValue, MetricValue as CoreMetricValue},
|
|
16
20
|
};
|
|
17
21
|
|
|
18
|
-
use bridge_macros::js_function;
|
|
22
|
+
use bridge_macros::{TryIntoJs, js_function};
|
|
23
|
+
use temporalio_sdk_core::telemetry::MetricsCallBuffer as CoreMetricsCallBuffer;
|
|
19
24
|
|
|
25
|
+
use crate::helpers::properties::ObjectExt as _;
|
|
26
|
+
use crate::helpers::try_into_js::{MemoizedHandle, OptionAsUndefined};
|
|
20
27
|
use crate::helpers::{
|
|
21
28
|
BridgeError, BridgeResult, JsonString, MutableFinalize, OpaqueInboundHandle,
|
|
22
|
-
OpaqueOutboundHandle,
|
|
29
|
+
OpaqueOutboundHandle, TryIntoJs,
|
|
23
30
|
};
|
|
24
31
|
use crate::runtime::Runtime;
|
|
25
32
|
|
|
33
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
34
|
+
// Metric Meter (aka Custom Metrics)
|
|
35
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
36
|
+
|
|
26
37
|
pub fn init(cx: &mut neon::prelude::ModuleContext) -> neon::prelude::NeonResult<()> {
|
|
27
38
|
cx.export_function("newMetricCounter", new_metric_counter)?;
|
|
28
39
|
cx.export_function("newMetricHistogram", new_metric_histogram)?;
|
|
@@ -116,12 +127,11 @@ pub fn new_metric_counter(
|
|
|
116
127
|
))?;
|
|
117
128
|
|
|
118
129
|
let counter = meter.inner.counter(
|
|
119
|
-
|
|
130
|
+
CoreMetricParameters::builder()
|
|
120
131
|
.name(name)
|
|
121
132
|
.unit(unit)
|
|
122
133
|
.description(description)
|
|
123
|
-
.build()
|
|
124
|
-
.context("Failed to build metric parameters")?,
|
|
134
|
+
.build(),
|
|
125
135
|
);
|
|
126
136
|
|
|
127
137
|
Ok(OpaqueOutboundHandle::new(Counter { meter, counter }))
|
|
@@ -143,12 +153,11 @@ pub fn new_metric_histogram(
|
|
|
143
153
|
))?;
|
|
144
154
|
|
|
145
155
|
let histogram = meter.inner.histogram(
|
|
146
|
-
|
|
156
|
+
CoreMetricParameters::builder()
|
|
147
157
|
.name(name)
|
|
148
158
|
.unit(unit)
|
|
149
159
|
.description(description)
|
|
150
|
-
.build()
|
|
151
|
-
.context("Failed to build metric parameters")?,
|
|
160
|
+
.build(),
|
|
152
161
|
);
|
|
153
162
|
|
|
154
163
|
Ok(OpaqueOutboundHandle::new(Histogram { meter, histogram }))
|
|
@@ -170,12 +179,11 @@ pub fn new_metric_histogram_f64(
|
|
|
170
179
|
))?;
|
|
171
180
|
|
|
172
181
|
let histogram = meter.inner.histogram_f64(
|
|
173
|
-
|
|
182
|
+
CoreMetricParameters::builder()
|
|
174
183
|
.name(name)
|
|
175
184
|
.unit(unit)
|
|
176
185
|
.description(description)
|
|
177
|
-
.build()
|
|
178
|
-
.context("Failed to build metric parameters")?,
|
|
186
|
+
.build(),
|
|
179
187
|
);
|
|
180
188
|
|
|
181
189
|
Ok(OpaqueOutboundHandle::new(HistogramF64 { meter, histogram }))
|
|
@@ -197,12 +205,11 @@ pub fn new_metric_gauge(
|
|
|
197
205
|
))?;
|
|
198
206
|
|
|
199
207
|
let gauge = meter.inner.gauge(
|
|
200
|
-
|
|
208
|
+
CoreMetricParameters::builder()
|
|
201
209
|
.name(name)
|
|
202
210
|
.unit(unit)
|
|
203
211
|
.description(description)
|
|
204
|
-
.build()
|
|
205
|
-
.context("Failed to build metric parameters")?,
|
|
212
|
+
.build(),
|
|
206
213
|
);
|
|
207
214
|
|
|
208
215
|
Ok(OpaqueOutboundHandle::new(Gauge { meter, gauge }))
|
|
@@ -224,12 +231,11 @@ pub fn new_metric_gauge_f64(
|
|
|
224
231
|
))?;
|
|
225
232
|
|
|
226
233
|
let gauge = meter.inner.gauge_f64(
|
|
227
|
-
|
|
234
|
+
CoreMetricParameters::builder()
|
|
228
235
|
.name(name)
|
|
229
236
|
.unit(unit)
|
|
230
237
|
.description(description)
|
|
231
|
-
.build()
|
|
232
|
-
.context("Failed to build metric parameters")?,
|
|
238
|
+
.build(),
|
|
233
239
|
);
|
|
234
240
|
|
|
235
241
|
Ok(OpaqueOutboundHandle::new(GaugeF64 { meter, gauge }))
|
|
@@ -245,10 +251,12 @@ pub fn add_metric_counter_value(
|
|
|
245
251
|
attributes: JsonString<MetricAttributes>,
|
|
246
252
|
) -> BridgeResult<()> {
|
|
247
253
|
let counter_handle = counter_handle.borrow()?;
|
|
254
|
+
|
|
248
255
|
let attributes = counter_handle
|
|
249
256
|
.meter
|
|
250
257
|
.inner
|
|
251
258
|
.new_attributes(parse_metric_attributes(attributes.value));
|
|
259
|
+
|
|
252
260
|
counter_handle.counter.add(value as u64, &attributes);
|
|
253
261
|
Ok(())
|
|
254
262
|
}
|
|
@@ -313,6 +321,235 @@ pub fn set_metric_gauge_f64_value(
|
|
|
313
321
|
Ok(())
|
|
314
322
|
}
|
|
315
323
|
|
|
324
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
325
|
+
// Buffered Metrics (aka lang-side metrics exporter)
|
|
326
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
327
|
+
|
|
328
|
+
#[derive(Debug)]
|
|
329
|
+
pub struct MetricsCallBuffer {
|
|
330
|
+
pub(crate) core_buffer: Arc<CoreMetricsCallBuffer<BufferedMetricRef>>,
|
|
331
|
+
use_seconds_for_durations: bool,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
impl MetricsCallBuffer {
|
|
335
|
+
pub(crate) fn new(max_buffer_size: usize, use_seconds_for_durations: bool) -> Self {
|
|
336
|
+
Self {
|
|
337
|
+
core_buffer: Arc::new(CoreMetricsCallBuffer::new(max_buffer_size)),
|
|
338
|
+
use_seconds_for_durations,
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
pub(crate) fn retrieve(&self) -> Vec<BufferedMetricUpdate> {
|
|
343
|
+
self.core_buffer
|
|
344
|
+
.retrieve()
|
|
345
|
+
.iter()
|
|
346
|
+
.filter_map(|e| self.convert_metric_event(e))
|
|
347
|
+
.collect()
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
fn convert_metric_event(
|
|
351
|
+
&self,
|
|
352
|
+
event: &CoreMetricEvent<BufferedMetricRef>,
|
|
353
|
+
) -> Option<BufferedMetricUpdate> {
|
|
354
|
+
match event {
|
|
355
|
+
CoreMetricEvent::Create {
|
|
356
|
+
params,
|
|
357
|
+
populate_into,
|
|
358
|
+
kind,
|
|
359
|
+
} => {
|
|
360
|
+
// Create the metric and put it on the lazy ref
|
|
361
|
+
let metric = BufferedMetric::new(params, *kind, self.use_seconds_for_durations);
|
|
362
|
+
populate_into
|
|
363
|
+
.set(Arc::new(BufferedMetricRef(MemoizedHandle::new(metric))))
|
|
364
|
+
.expect("Unable to set buffered metric on reference");
|
|
365
|
+
|
|
366
|
+
None
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Create the attributes and put it on the lazy ref
|
|
370
|
+
CoreMetricEvent::CreateAttributes {
|
|
371
|
+
populate_into,
|
|
372
|
+
append_from,
|
|
373
|
+
attributes,
|
|
374
|
+
} => {
|
|
375
|
+
let append_from = append_from.as_ref().map(|f| {
|
|
376
|
+
f.get()
|
|
377
|
+
.clone()
|
|
378
|
+
.as_any()
|
|
379
|
+
.downcast::<BufferedMetricAttributesRef>()
|
|
380
|
+
.expect("Unable to downcast to expected buffered metric attributes")
|
|
381
|
+
});
|
|
382
|
+
let attributes = BufferedMetricAttributes {
|
|
383
|
+
new_attributes: attributes.clone(),
|
|
384
|
+
append_from: append_from.map(|f| f.as_ref().clone()),
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
let r = BufferedMetricAttributesRef(MemoizedHandle::new(attributes));
|
|
388
|
+
populate_into
|
|
389
|
+
.set(Arc::new(r))
|
|
390
|
+
.expect("Unable to set buffered metric attributes on reference");
|
|
391
|
+
|
|
392
|
+
None
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
CoreMetricEvent::Update {
|
|
396
|
+
instrument,
|
|
397
|
+
attributes,
|
|
398
|
+
update,
|
|
399
|
+
} => Some(BufferedMetricUpdate {
|
|
400
|
+
metric: instrument.get().as_ref().clone(),
|
|
401
|
+
#[allow(clippy::match_same_arms, clippy::cast_precision_loss)]
|
|
402
|
+
value: match update {
|
|
403
|
+
metrics::MetricUpdateVal::Duration(v) if self.use_seconds_for_durations => {
|
|
404
|
+
v.as_secs_f64()
|
|
405
|
+
}
|
|
406
|
+
metrics::MetricUpdateVal::Duration(v) => v.as_millis() as f64,
|
|
407
|
+
metrics::MetricUpdateVal::Delta(v) => *v as f64,
|
|
408
|
+
metrics::MetricUpdateVal::DeltaF64(v) => *v,
|
|
409
|
+
metrics::MetricUpdateVal::Value(v) => *v as f64,
|
|
410
|
+
metrics::MetricUpdateVal::ValueF64(v) => *v,
|
|
411
|
+
},
|
|
412
|
+
attributes: attributes
|
|
413
|
+
.get()
|
|
414
|
+
.clone()
|
|
415
|
+
.as_any()
|
|
416
|
+
.downcast::<BufferedMetricAttributesRef>()
|
|
417
|
+
.expect("Unable to downcast to expected buffered metric attributes")
|
|
418
|
+
.as_ref()
|
|
419
|
+
.clone(),
|
|
420
|
+
}),
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
#[derive(TryIntoJs)]
|
|
426
|
+
pub struct BufferedMetricUpdate {
|
|
427
|
+
metric: BufferedMetricRef,
|
|
428
|
+
value: f64,
|
|
429
|
+
attributes: BufferedMetricAttributesRef,
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
#[derive(TryIntoJs, Clone, Debug)]
|
|
433
|
+
struct BufferedMetric {
|
|
434
|
+
name: String,
|
|
435
|
+
description: OptionAsUndefined<String>,
|
|
436
|
+
unit: OptionAsUndefined<String>,
|
|
437
|
+
kind: MetricKind,
|
|
438
|
+
value_type: MetricValueType,
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
impl BufferedMetric {
|
|
442
|
+
pub fn new(
|
|
443
|
+
params: &CoreMetricParameters,
|
|
444
|
+
kind: CoreMetricKind,
|
|
445
|
+
use_seconds_for_durations: bool,
|
|
446
|
+
) -> Self {
|
|
447
|
+
let unit = match kind {
|
|
448
|
+
CoreMetricKind::HistogramDuration if params.unit == "duration" => {
|
|
449
|
+
Some((if use_seconds_for_durations { "s" } else { "ms" }).to_string())
|
|
450
|
+
}
|
|
451
|
+
_ => (!params.unit.is_empty()).then_some(params.unit.to_string()),
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
#[allow(clippy::match_same_arms)]
|
|
455
|
+
let (kind, value_type) = match kind {
|
|
456
|
+
CoreMetricKind::Counter => (MetricKind::Counter, MetricValueType::Int),
|
|
457
|
+
CoreMetricKind::Gauge => (MetricKind::Gauge, MetricValueType::Int),
|
|
458
|
+
CoreMetricKind::GaugeF64 => (MetricKind::Gauge, MetricValueType::Float),
|
|
459
|
+
CoreMetricKind::Histogram => (MetricKind::Histogram, MetricValueType::Int),
|
|
460
|
+
CoreMetricKind::HistogramF64 => (MetricKind::Histogram, MetricValueType::Float),
|
|
461
|
+
CoreMetricKind::HistogramDuration => (MetricKind::Histogram, MetricValueType::Int),
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
let description =
|
|
465
|
+
(!params.description.is_empty()).then_some(params.description.to_string());
|
|
466
|
+
|
|
467
|
+
Self {
|
|
468
|
+
name: params.name.to_string(),
|
|
469
|
+
description: description.into(),
|
|
470
|
+
unit: unit.into(),
|
|
471
|
+
kind,
|
|
472
|
+
value_type,
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
#[derive(Clone, Debug)]
|
|
478
|
+
pub struct BufferedMetricRef(MemoizedHandle<BufferedMetric>);
|
|
479
|
+
impl CoreBufferInstrumentRef for BufferedMetricRef {}
|
|
480
|
+
|
|
481
|
+
impl TryIntoJs for BufferedMetricRef {
|
|
482
|
+
type Output = JsObject;
|
|
483
|
+
fn try_into_js<'cx>(self, cx: &mut impl Context<'cx>) -> JsResult<'cx, Self::Output> {
|
|
484
|
+
self.0.try_into_js(cx)
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
#[derive(Clone, Debug)]
|
|
489
|
+
struct BufferedMetricAttributes {
|
|
490
|
+
new_attributes: Vec<CoreMetricKeyValue>,
|
|
491
|
+
append_from: Option<BufferedMetricAttributesRef>,
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
impl TryIntoJs for BufferedMetricAttributes {
|
|
495
|
+
type Output = JsObject;
|
|
496
|
+
fn try_into_js<'cx>(self, cx: &mut impl Context<'cx>) -> JsResult<'cx, Self::Output> {
|
|
497
|
+
let object = cx.empty_object();
|
|
498
|
+
|
|
499
|
+
// Copy existing attributes, if any
|
|
500
|
+
if let Some(existing) = self.append_from {
|
|
501
|
+
let existing_attrs = existing.try_into_js(cx)?;
|
|
502
|
+
|
|
503
|
+
object_assign(cx, object, existing_attrs)?;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Assign new attributes
|
|
507
|
+
for kv in self.new_attributes {
|
|
508
|
+
let k = kv.key.as_str();
|
|
509
|
+
#[allow(clippy::cast_precision_loss)]
|
|
510
|
+
match &kv.value {
|
|
511
|
+
metrics::MetricValue::String(v) => object.set_property_from(cx, k, v.as_str()),
|
|
512
|
+
metrics::MetricValue::Int(v) => object.set_property_from(cx, k, *v as f64),
|
|
513
|
+
metrics::MetricValue::Float(v) => object.set_property_from(cx, k, *v),
|
|
514
|
+
metrics::MetricValue::Bool(v) => object.set_property_from(cx, k, *v),
|
|
515
|
+
}?;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
Ok(object)
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
#[derive(Clone, Debug)]
|
|
523
|
+
struct BufferedMetricAttributesRef(MemoizedHandle<BufferedMetricAttributes>);
|
|
524
|
+
|
|
525
|
+
impl CustomMetricAttributes for BufferedMetricAttributesRef {
|
|
526
|
+
fn as_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
|
|
527
|
+
self as Arc<dyn Any + Send + Sync>
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
impl TryIntoJs for BufferedMetricAttributesRef {
|
|
532
|
+
type Output = JsObject;
|
|
533
|
+
fn try_into_js<'cx>(self, cx: &mut impl Context<'cx>) -> JsResult<'cx, Self::Output> {
|
|
534
|
+
self.0.try_into_js(cx)
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
#[derive(TryIntoJs, Clone, Debug)]
|
|
539
|
+
enum MetricKind {
|
|
540
|
+
Counter,
|
|
541
|
+
Gauge,
|
|
542
|
+
Histogram,
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
#[derive(TryIntoJs, Clone, Debug)]
|
|
546
|
+
enum MetricValueType {
|
|
547
|
+
Int,
|
|
548
|
+
Float,
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
552
|
+
// Helpers
|
|
316
553
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
317
554
|
|
|
318
555
|
fn parse_metric_attributes(attrs: MetricAttributes) -> NewAttributes {
|
|
@@ -326,3 +563,16 @@ fn parse_metric_attributes(attrs: MetricAttributes) -> NewAttributes {
|
|
|
326
563
|
.collect();
|
|
327
564
|
NewAttributes { attributes: attrs }
|
|
328
565
|
}
|
|
566
|
+
|
|
567
|
+
fn object_assign<'cx>(
|
|
568
|
+
cx: &mut impl Context<'cx>,
|
|
569
|
+
object: Handle<'cx, JsObject>,
|
|
570
|
+
source: Handle<'cx, JsObject>,
|
|
571
|
+
) -> JsResult<'cx, JsObject> {
|
|
572
|
+
let object_class = cx.global::<JsFunction>("Object")?;
|
|
573
|
+
let assign_function = object_class.get::<JsFunction, _, _>(cx, "assign")?;
|
|
574
|
+
let null = cx.null();
|
|
575
|
+
assign_function.call(cx, null, vec![object.upcast(), source.upcast()])?;
|
|
576
|
+
|
|
577
|
+
Ok(object)
|
|
578
|
+
}
|
package/src/runtime.rs
CHANGED
|
@@ -10,7 +10,7 @@ use temporalio_common::telemetry::{
|
|
|
10
10
|
PrometheusExporterOptions as CorePrometheusExporterOptions, metrics::CoreMeter,
|
|
11
11
|
};
|
|
12
12
|
use temporalio_sdk_core::{
|
|
13
|
-
CoreRuntime,
|
|
13
|
+
CoreRuntime, TokioRuntimeBuilder,
|
|
14
14
|
telemetry::{build_otlp_metric_exporter, start_prometheus_metric_exporter},
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -20,6 +20,7 @@ use tokio_stream::StreamExt as _;
|
|
|
20
20
|
use crate::{
|
|
21
21
|
helpers::{handles::MutableFinalize, *},
|
|
22
22
|
logs::LogEntry,
|
|
23
|
+
metrics::{BufferedMetricUpdate, MetricsCallBuffer},
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
#[macro_export]
|
|
@@ -36,6 +37,10 @@ macro_rules! enter_sync {
|
|
|
36
37
|
pub fn init(cx: &mut neon::prelude::ModuleContext) -> neon::prelude::NeonResult<()> {
|
|
37
38
|
cx.export_function("newRuntime", runtime_new)?;
|
|
38
39
|
cx.export_function("runtimeShutdown", runtime_shutdown)?;
|
|
40
|
+
cx.export_function(
|
|
41
|
+
"runtimeRetrieveBufferedMetrics",
|
|
42
|
+
runtime_retrieve_buffered_metrics,
|
|
43
|
+
)?;
|
|
39
44
|
|
|
40
45
|
Ok(())
|
|
41
46
|
}
|
|
@@ -51,6 +56,9 @@ pub struct Runtime {
|
|
|
51
56
|
// For some unknown reason, the otel metrics exporter will go crazy on shutdown in some
|
|
52
57
|
// scenarios if we don't hold on to the `CoreOtelMeter` till the `Runtime` finally gets dropped.
|
|
53
58
|
_otel_metrics_exporter: Option<Arc<dyn CoreMeter + 'static>>,
|
|
59
|
+
|
|
60
|
+
// Buffered metrics call buffer, if buffered metrics are enabled
|
|
61
|
+
pub(crate) metrics_call_buffer: Option<MetricsCallBuffer>,
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
/// Initialize Core global telemetry and create the tokio runtime required to run Core.
|
|
@@ -63,41 +71,58 @@ pub fn runtime_new(
|
|
|
63
71
|
bridge_options.try_into()?;
|
|
64
72
|
|
|
65
73
|
// Create core runtime which starts tokio multi-thread runtime
|
|
66
|
-
let runtime_options =
|
|
74
|
+
let runtime_options = temporalio_sdk_core::RuntimeOptions::builder()
|
|
67
75
|
.telemetry_options(telemetry_options)
|
|
68
76
|
.heartbeat_interval(worker_heartbeat_interval_millis.map(Duration::from_millis))
|
|
69
77
|
.build()
|
|
70
|
-
.
|
|
78
|
+
.map_err(|err| BridgeError::TypeError {
|
|
79
|
+
message: format!("Failed to build runtime options: {err}"),
|
|
80
|
+
field: None,
|
|
81
|
+
})?;
|
|
71
82
|
let mut core_runtime = CoreRuntime::new(runtime_options, TokioRuntimeBuilder::default())
|
|
72
83
|
.context("Failed to initialize Core Runtime")?;
|
|
73
84
|
|
|
74
85
|
enter_sync!(core_runtime);
|
|
75
86
|
|
|
76
87
|
// Run the metrics exporter task, if needed. Created after Runtime since it needs Tokio handle
|
|
77
|
-
let (prom_metrics_exporter_task, otel_metrics_exporter) =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
let (prom_metrics_exporter_task, otel_metrics_exporter, metrics_call_buffer) =
|
|
89
|
+
match metrics_options {
|
|
90
|
+
Some(BridgeMetricsExporter::Prometheus(prom_opts)) => {
|
|
91
|
+
let exporter = start_prometheus_metric_exporter(prom_opts)
|
|
92
|
+
.context("Failed to start prometheus metrics exporter")?;
|
|
81
93
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
core_runtime
|
|
95
|
+
.telemetry_mut()
|
|
96
|
+
.attach_late_init_metrics(exporter.meter);
|
|
85
97
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
(Some(exporter.abort_handle), None, None)
|
|
99
|
+
}
|
|
100
|
+
Some(BridgeMetricsExporter::Otel(otel_opts)) => {
|
|
101
|
+
let exporter = build_otlp_metric_exporter(otel_opts)
|
|
102
|
+
.context("Failed to start OTel metrics exporter")?;
|
|
91
103
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
let exporter: Arc<dyn CoreMeter + 'static> = Arc::new(exporter);
|
|
105
|
+
core_runtime
|
|
106
|
+
.telemetry_mut()
|
|
107
|
+
.attach_late_init_metrics(exporter.clone());
|
|
96
108
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
(None, Some(exporter), None)
|
|
110
|
+
}
|
|
111
|
+
Some(BridgeMetricsExporter::Buffer {
|
|
112
|
+
max_buffer_size,
|
|
113
|
+
use_seconds_for_durations,
|
|
114
|
+
}) => {
|
|
115
|
+
let metrics_call_buffer =
|
|
116
|
+
MetricsCallBuffer::new(max_buffer_size, use_seconds_for_durations);
|
|
117
|
+
|
|
118
|
+
core_runtime
|
|
119
|
+
.telemetry_mut()
|
|
120
|
+
.attach_late_init_metrics(metrics_call_buffer.core_buffer.clone());
|
|
121
|
+
|
|
122
|
+
(None, None, Some(metrics_call_buffer))
|
|
123
|
+
}
|
|
124
|
+
None => (None, None, None),
|
|
125
|
+
};
|
|
101
126
|
|
|
102
127
|
// Run the log exporter task, if needed. Created after Runtime since it needs Tokio handle.
|
|
103
128
|
let log_exporter_task = if let BridgeLogExporter::Push { stream, receiver } = logging_options {
|
|
@@ -127,6 +152,7 @@ pub fn runtime_new(
|
|
|
127
152
|
log_exporter_task,
|
|
128
153
|
metrics_exporter_task: prom_metrics_exporter_task.map(Arc::new),
|
|
129
154
|
_otel_metrics_exporter: otel_metrics_exporter,
|
|
155
|
+
metrics_call_buffer,
|
|
130
156
|
}))
|
|
131
157
|
}
|
|
132
158
|
|
|
@@ -140,6 +166,24 @@ pub fn runtime_shutdown(runtime: OpaqueInboundHandle<Runtime>) -> BridgeResult<(
|
|
|
140
166
|
Ok(())
|
|
141
167
|
}
|
|
142
168
|
|
|
169
|
+
/// Retrieve buffered metrics from the runtime.
|
|
170
|
+
///
|
|
171
|
+
/// This function drains the metrics buffer and returns all metric updates that have been
|
|
172
|
+
/// accumulated since the last call to this function.
|
|
173
|
+
#[js_function]
|
|
174
|
+
pub fn runtime_retrieve_buffered_metrics(
|
|
175
|
+
runtime: OpaqueInboundHandle<Runtime>,
|
|
176
|
+
) -> BridgeResult<Vec<BufferedMetricUpdate>> {
|
|
177
|
+
let runtime = runtime.borrow()?;
|
|
178
|
+
let buffer = runtime.metrics_call_buffer.as_ref().ok_or_else(|| {
|
|
179
|
+
BridgeError::UnexpectedError(
|
|
180
|
+
"Attempting to retrieve buffered metrics of a runtime without buffer".into(),
|
|
181
|
+
)
|
|
182
|
+
})?;
|
|
183
|
+
|
|
184
|
+
Ok(buffer.retrieve())
|
|
185
|
+
}
|
|
186
|
+
|
|
143
187
|
/// Drop will handle the cleanup
|
|
144
188
|
impl MutableFinalize for Runtime {}
|
|
145
189
|
|
|
@@ -223,6 +267,10 @@ impl RuntimeExt for Arc<CoreRuntime> {
|
|
|
223
267
|
pub enum BridgeMetricsExporter {
|
|
224
268
|
Prometheus(CorePrometheusExporterOptions),
|
|
225
269
|
Otel(CoreOtelCollectorOptions),
|
|
270
|
+
Buffer {
|
|
271
|
+
max_buffer_size: usize,
|
|
272
|
+
use_seconds_for_durations: bool,
|
|
273
|
+
},
|
|
226
274
|
}
|
|
227
275
|
|
|
228
276
|
pub enum BridgeLogExporter {
|
|
@@ -238,15 +286,12 @@ pub enum BridgeLogExporter {
|
|
|
238
286
|
mod config {
|
|
239
287
|
use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration};
|
|
240
288
|
|
|
241
|
-
use anyhow::Context as _;
|
|
242
|
-
|
|
243
289
|
use neon::prelude::*;
|
|
244
290
|
use temporalio_common::telemetry::{
|
|
245
291
|
HistogramBucketOverrides, Logger as CoreTelemetryLogger, MetricTemporality,
|
|
246
|
-
OtelCollectorOptions as CoreOtelCollectorOptions,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
TelemetryOptionsBuilder,
|
|
292
|
+
OtelCollectorOptions as CoreOtelCollectorOptions, OtlpProtocol,
|
|
293
|
+
PrometheusExporterOptions as CorePrometheusExporterOptions,
|
|
294
|
+
TelemetryOptions as CoreTelemetryOptions,
|
|
250
295
|
};
|
|
251
296
|
use temporalio_sdk_core::{Url, telemetry::CoreLogStreamConsumer};
|
|
252
297
|
|
|
@@ -291,6 +336,7 @@ mod config {
|
|
|
291
336
|
pub(super) enum MetricsExporterOptions {
|
|
292
337
|
Prometheus(PrometheusMetricsExporterConfig),
|
|
293
338
|
Otel(OtelMetricsExporterConfig),
|
|
339
|
+
Buffer(BufferedMetricsExporterConfig),
|
|
294
340
|
}
|
|
295
341
|
|
|
296
342
|
#[derive(Debug, Clone, TryFromJs)]
|
|
@@ -315,6 +361,12 @@ mod config {
|
|
|
315
361
|
protocol: StringEncoded<OtlpProtocol>,
|
|
316
362
|
}
|
|
317
363
|
|
|
364
|
+
#[derive(Debug, Clone, TryFromJs)]
|
|
365
|
+
pub(super) struct BufferedMetricsExporterConfig {
|
|
366
|
+
max_buffer_size: usize,
|
|
367
|
+
use_seconds_for_durations: bool,
|
|
368
|
+
}
|
|
369
|
+
|
|
318
370
|
/// A private newtype so that we can implement `TryFromJs` on simple externally defined enums
|
|
319
371
|
#[derive(Debug, Clone)]
|
|
320
372
|
struct StringEncoded<T>(T);
|
|
@@ -360,13 +412,11 @@ mod config {
|
|
|
360
412
|
}
|
|
361
413
|
};
|
|
362
414
|
|
|
363
|
-
let
|
|
364
|
-
let telemetry_options = telemetry_options
|
|
415
|
+
let telemetry_options = CoreTelemetryOptions::builder()
|
|
365
416
|
.logging(telemetry_logger)
|
|
366
417
|
.metric_prefix(telemetry.metric_prefix)
|
|
367
418
|
.attach_service_name(telemetry.attach_service_name)
|
|
368
|
-
.build()
|
|
369
|
-
.context("Failed to build telemetry options")?;
|
|
419
|
+
.build();
|
|
370
420
|
|
|
371
421
|
let metrics_exporter = metrics_exporter
|
|
372
422
|
.map(std::convert::TryInto::try_into)
|
|
@@ -389,6 +439,10 @@ mod config {
|
|
|
389
439
|
Ok(super::BridgeMetricsExporter::Prometheus(prom.try_into()?))
|
|
390
440
|
}
|
|
391
441
|
Self::Otel(otel) => Ok(super::BridgeMetricsExporter::Otel(otel.try_into()?)),
|
|
442
|
+
Self::Buffer(buffered) => Ok(super::BridgeMetricsExporter::Buffer {
|
|
443
|
+
max_buffer_size: buffered.max_buffer_size,
|
|
444
|
+
use_seconds_for_durations: buffered.use_seconds_for_durations,
|
|
445
|
+
}),
|
|
392
446
|
}
|
|
393
447
|
}
|
|
394
448
|
}
|
|
@@ -397,8 +451,7 @@ mod config {
|
|
|
397
451
|
type Error = BridgeError;
|
|
398
452
|
|
|
399
453
|
fn try_into(self) -> BridgeResult<CorePrometheusExporterOptions> {
|
|
400
|
-
let
|
|
401
|
-
let options = options
|
|
454
|
+
let options = CorePrometheusExporterOptions::builder()
|
|
402
455
|
.socket_addr(self.socket_addr)
|
|
403
456
|
.counters_total_suffix(self.counters_total_suffix)
|
|
404
457
|
.unit_suffix(self.unit_suffix)
|
|
@@ -407,8 +460,7 @@ mod config {
|
|
|
407
460
|
overrides: self.histogram_bucket_overrides,
|
|
408
461
|
})
|
|
409
462
|
.global_tags(self.global_tags)
|
|
410
|
-
.build()
|
|
411
|
-
.context("Failed to build prometheus exporter options")?;
|
|
463
|
+
.build();
|
|
412
464
|
|
|
413
465
|
Ok(options)
|
|
414
466
|
}
|
|
@@ -418,8 +470,7 @@ mod config {
|
|
|
418
470
|
type Error = BridgeError;
|
|
419
471
|
|
|
420
472
|
fn try_into(self) -> BridgeResult<CoreOtelCollectorOptions> {
|
|
421
|
-
let
|
|
422
|
-
let options = options
|
|
473
|
+
let options = CoreOtelCollectorOptions::builder()
|
|
423
474
|
.url(self.url)
|
|
424
475
|
.protocol(*self.protocol)
|
|
425
476
|
.headers(self.headers)
|
|
@@ -430,8 +481,7 @@ mod config {
|
|
|
430
481
|
overrides: self.histogram_bucket_overrides,
|
|
431
482
|
})
|
|
432
483
|
.global_tags(self.global_tags)
|
|
433
|
-
.build()
|
|
434
|
-
.context("Failed to build otel exporter options")?;
|
|
484
|
+
.build();
|
|
435
485
|
|
|
436
486
|
Ok(options)
|
|
437
487
|
}
|