@temporalio/core-bridge 1.12.0 → 1.12.2
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 +64 -119
- package/Cargo.toml +1 -1
- package/index.js +3 -2
- package/package.json +3 -3
- 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/config.toml +1 -2
- package/sdk-core/.github/workflows/per-pr.yml +2 -0
- package/sdk-core/AGENTS.md +7 -0
- package/sdk-core/Cargo.toml +9 -5
- package/sdk-core/README.md +6 -5
- package/sdk-core/client/Cargo.toml +3 -2
- package/sdk-core/client/src/lib.rs +17 -8
- package/sdk-core/client/src/metrics.rs +57 -23
- package/sdk-core/client/src/raw.rs +33 -15
- package/sdk-core/core/Cargo.toml +11 -9
- package/sdk-core/core/benches/workflow_replay.rs +114 -15
- package/sdk-core/core/src/core_tests/activity_tasks.rs +18 -18
- package/sdk-core/core/src/core_tests/child_workflows.rs +4 -4
- package/sdk-core/core/src/core_tests/determinism.rs +6 -6
- package/sdk-core/core/src/core_tests/local_activities.rs +20 -20
- package/sdk-core/core/src/core_tests/mod.rs +40 -5
- package/sdk-core/core/src/core_tests/queries.rs +25 -16
- package/sdk-core/core/src/core_tests/replay_flag.rs +3 -3
- package/sdk-core/core/src/core_tests/updates.rs +3 -3
- package/sdk-core/core/src/core_tests/workers.rs +9 -7
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +40 -42
- package/sdk-core/core/src/ephemeral_server/mod.rs +1 -19
- package/sdk-core/core/src/lib.rs +10 -1
- package/sdk-core/core/src/pollers/poll_buffer.rs +2 -2
- package/sdk-core/core/src/replay/mod.rs +3 -3
- package/sdk-core/core/src/telemetry/metrics.rs +306 -152
- package/sdk-core/core/src/telemetry/mod.rs +11 -4
- package/sdk-core/core/src/telemetry/otel.rs +134 -131
- package/sdk-core/core/src/telemetry/prometheus_meter.rs +885 -0
- package/sdk-core/core/src/telemetry/prometheus_server.rs +48 -28
- package/sdk-core/core/src/test_help/mod.rs +27 -12
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +7 -7
- package/sdk-core/core/src/worker/activities.rs +4 -4
- package/sdk-core/core/src/worker/client/mocks.rs +10 -3
- package/sdk-core/core/src/worker/client.rs +68 -5
- package/sdk-core/core/src/worker/heartbeat.rs +229 -0
- package/sdk-core/core/src/worker/mod.rs +35 -14
- package/sdk-core/core/src/worker/tuner/resource_based.rs +4 -4
- package/sdk-core/core/src/worker/workflow/history_update.rs +71 -19
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -2
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -1
- package/sdk-core/core/src/worker/workflow/machines/nexus_operation_state_machine.rs +31 -48
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -2
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +3 -3
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +4 -1
- package/sdk-core/core/src/worker/workflow/managed_run.rs +1 -1
- package/sdk-core/core/src/worker/workflow/mod.rs +15 -15
- package/sdk-core/core-api/Cargo.toml +2 -2
- package/sdk-core/core-api/src/envconfig.rs +204 -99
- package/sdk-core/core-api/src/lib.rs +9 -0
- package/sdk-core/core-api/src/telemetry/metrics.rs +548 -100
- package/sdk-core/core-api/src/worker.rs +11 -5
- package/sdk-core/core-c-bridge/Cargo.toml +49 -0
- package/sdk-core/core-c-bridge/build.rs +26 -0
- package/sdk-core/core-c-bridge/include/temporal-sdk-core-c-bridge.h +817 -0
- package/sdk-core/core-c-bridge/src/client.rs +679 -0
- package/sdk-core/core-c-bridge/src/lib.rs +245 -0
- package/sdk-core/core-c-bridge/src/metric.rs +682 -0
- package/sdk-core/core-c-bridge/src/random.rs +61 -0
- package/sdk-core/core-c-bridge/src/runtime.rs +445 -0
- package/sdk-core/core-c-bridge/src/testing.rs +282 -0
- package/sdk-core/core-c-bridge/src/tests/context.rs +644 -0
- package/sdk-core/core-c-bridge/src/tests/mod.rs +178 -0
- package/sdk-core/core-c-bridge/src/tests/utils.rs +108 -0
- package/sdk-core/core-c-bridge/src/worker.rs +1069 -0
- package/sdk-core/etc/deps.svg +64 -64
- package/sdk-core/sdk/src/activity_context.rs +6 -4
- package/sdk-core/sdk/src/lib.rs +49 -27
- package/sdk-core/sdk/src/workflow_future.rs +18 -25
- package/sdk-core/sdk-core-protos/protos/api_upstream/README.md +4 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +0 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +630 -83
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +632 -78
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +4 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +6 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +2 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/deployment/v1/message.proto +32 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/common.proto +10 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/deployment.proto +26 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +4 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +47 -31
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +4 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +7 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/worker/v1/message.proto +134 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +14 -11
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +148 -37
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +21 -0
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -4
- package/sdk-core/sdk-core-protos/src/history_builder.rs +9 -5
- package/sdk-core/sdk-core-protos/src/lib.rs +96 -6
- package/sdk-core/test-utils/src/lib.rs +11 -3
- package/sdk-core/tests/cloud_tests.rs +3 -3
- package/sdk-core/tests/heavy_tests.rs +11 -3
- package/sdk-core/tests/integ_tests/client_tests.rs +12 -13
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/metrics_tests.rs +188 -83
- package/sdk-core/tests/integ_tests/polling_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/queries_tests.rs +56 -40
- package/sdk-core/tests/integ_tests/update_tests.rs +2 -7
- package/sdk-core/tests/integ_tests/worker_tests.rs +3 -4
- package/sdk-core/tests/integ_tests/worker_versioning_tests.rs +3 -7
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +3 -5
- package/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +24 -17
- package/src/client.rs +6 -0
- package/src/metrics.rs +6 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use crate::{AttachMetricLabels, CallType};
|
|
1
|
+
use crate::{AttachMetricLabels, CallType, dbg_panic};
|
|
2
2
|
use futures_util::{FutureExt, future::BoxFuture};
|
|
3
3
|
use std::{
|
|
4
4
|
sync::Arc,
|
|
@@ -6,10 +6,10 @@ use std::{
|
|
|
6
6
|
time::{Duration, Instant},
|
|
7
7
|
};
|
|
8
8
|
use temporal_sdk_core_api::telemetry::metrics::{
|
|
9
|
-
CoreMeter, Counter,
|
|
10
|
-
TemporalMeter,
|
|
9
|
+
CoreMeter, Counter, CounterBase, HistogramDuration, HistogramDurationBase, MetricAttributable,
|
|
10
|
+
MetricAttributes, MetricKeyValue, MetricParameters, TemporalMeter,
|
|
11
11
|
};
|
|
12
|
-
use tonic::{Code, body::
|
|
12
|
+
use tonic::{Code, body::Body, transport::Channel};
|
|
13
13
|
use tower::Service;
|
|
14
14
|
|
|
15
15
|
/// The string name (which may be prefixed) for this metric
|
|
@@ -26,22 +26,24 @@ pub(crate) struct MetricsContext {
|
|
|
26
26
|
meter: Arc<dyn CoreMeter>,
|
|
27
27
|
kvs: MetricAttributes,
|
|
28
28
|
poll_is_long: bool,
|
|
29
|
+
instruments: Instruments,
|
|
30
|
+
}
|
|
31
|
+
#[derive(Clone)]
|
|
32
|
+
struct Instruments {
|
|
33
|
+
svc_request: Counter,
|
|
34
|
+
svc_request_failed: Counter,
|
|
35
|
+
long_svc_request: Counter,
|
|
36
|
+
long_svc_request_failed: Counter,
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
long_svc_request: Arc<dyn Counter>,
|
|
33
|
-
long_svc_request_failed: Arc<dyn Counter>,
|
|
34
|
-
|
|
35
|
-
svc_request_latency: Arc<dyn HistogramDuration>,
|
|
36
|
-
long_svc_request_latency: Arc<dyn HistogramDuration>,
|
|
38
|
+
svc_request_latency: HistogramDuration,
|
|
39
|
+
long_svc_request_latency: HistogramDuration,
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
impl MetricsContext {
|
|
40
43
|
pub(crate) fn new(tm: TemporalMeter) -> Self {
|
|
41
44
|
let meter = tm.inner;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
poll_is_long: false,
|
|
45
|
+
let kvs = meter.new_attributes(tm.default_attribs);
|
|
46
|
+
let instruments = Instruments {
|
|
45
47
|
svc_request: meter.counter(MetricParameters {
|
|
46
48
|
name: "request".into(),
|
|
47
49
|
description: "Count of client request successes by rpc name".into(),
|
|
@@ -72,6 +74,11 @@ impl MetricsContext {
|
|
|
72
74
|
unit: "duration".into(),
|
|
73
75
|
description: "Histogram of client long-poll request latencies".into(),
|
|
74
76
|
}),
|
|
77
|
+
};
|
|
78
|
+
Self {
|
|
79
|
+
kvs,
|
|
80
|
+
poll_is_long: false,
|
|
81
|
+
instruments,
|
|
75
82
|
meter,
|
|
76
83
|
}
|
|
77
84
|
}
|
|
@@ -81,6 +88,33 @@ impl MetricsContext {
|
|
|
81
88
|
self.kvs = self
|
|
82
89
|
.meter
|
|
83
90
|
.extend_attributes(self.kvs.clone(), new_kvs.into());
|
|
91
|
+
|
|
92
|
+
let _ = self
|
|
93
|
+
.instruments
|
|
94
|
+
.svc_request
|
|
95
|
+
.with_attributes(&self.kvs)
|
|
96
|
+
.and_then(|v| {
|
|
97
|
+
self.instruments.svc_request = v;
|
|
98
|
+
self.instruments.long_svc_request.with_attributes(&self.kvs)
|
|
99
|
+
})
|
|
100
|
+
.and_then(|v| {
|
|
101
|
+
self.instruments.long_svc_request = v;
|
|
102
|
+
self.instruments
|
|
103
|
+
.svc_request_latency
|
|
104
|
+
.with_attributes(&self.kvs)
|
|
105
|
+
})
|
|
106
|
+
.and_then(|v| {
|
|
107
|
+
self.instruments.svc_request_latency = v;
|
|
108
|
+
self.instruments
|
|
109
|
+
.long_svc_request_latency
|
|
110
|
+
.with_attributes(&self.kvs)
|
|
111
|
+
})
|
|
112
|
+
.map(|v| {
|
|
113
|
+
self.instruments.long_svc_request_latency = v;
|
|
114
|
+
})
|
|
115
|
+
.inspect_err(|e| {
|
|
116
|
+
dbg_panic!("Failed to extend client metrics attributes: {:?}", e);
|
|
117
|
+
});
|
|
84
118
|
}
|
|
85
119
|
|
|
86
120
|
pub(crate) fn set_is_long_poll(&mut self) {
|
|
@@ -90,9 +124,9 @@ impl MetricsContext {
|
|
|
90
124
|
/// A request to the temporal service was made
|
|
91
125
|
pub(crate) fn svc_request(&self) {
|
|
92
126
|
if self.poll_is_long {
|
|
93
|
-
self.long_svc_request.
|
|
127
|
+
self.instruments.long_svc_request.adds(1);
|
|
94
128
|
} else {
|
|
95
|
-
self.svc_request.
|
|
129
|
+
self.instruments.svc_request.adds(1);
|
|
96
130
|
}
|
|
97
131
|
}
|
|
98
132
|
|
|
@@ -108,18 +142,18 @@ impl MetricsContext {
|
|
|
108
142
|
&self.kvs
|
|
109
143
|
};
|
|
110
144
|
if self.poll_is_long {
|
|
111
|
-
self.long_svc_request_failed.add(1, kvs);
|
|
145
|
+
self.instruments.long_svc_request_failed.add(1, kvs);
|
|
112
146
|
} else {
|
|
113
|
-
self.svc_request_failed.add(1, kvs);
|
|
147
|
+
self.instruments.svc_request_failed.add(1, kvs);
|
|
114
148
|
}
|
|
115
149
|
}
|
|
116
150
|
|
|
117
151
|
/// Record service request latency
|
|
118
152
|
pub(crate) fn record_svc_req_latency(&self, dur: Duration) {
|
|
119
153
|
if self.poll_is_long {
|
|
120
|
-
self.long_svc_request_latency.
|
|
154
|
+
self.instruments.long_svc_request_latency.records(dur);
|
|
121
155
|
} else {
|
|
122
|
-
self.svc_request_latency.
|
|
156
|
+
self.instruments.svc_request_latency.records(dur);
|
|
123
157
|
}
|
|
124
158
|
}
|
|
125
159
|
}
|
|
@@ -177,8 +211,8 @@ pub struct GrpcMetricSvc {
|
|
|
177
211
|
pub(crate) disable_errcode_label: bool,
|
|
178
212
|
}
|
|
179
213
|
|
|
180
|
-
impl Service<http::Request<
|
|
181
|
-
type Response = http::Response<
|
|
214
|
+
impl Service<http::Request<Body>> for GrpcMetricSvc {
|
|
215
|
+
type Response = http::Response<Body>;
|
|
182
216
|
type Error = tonic::transport::Error;
|
|
183
217
|
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
|
|
184
218
|
|
|
@@ -186,7 +220,7 @@ impl Service<http::Request<BoxBody>> for GrpcMetricSvc {
|
|
|
186
220
|
self.inner.poll_ready(cx).map_err(Into::into)
|
|
187
221
|
}
|
|
188
222
|
|
|
189
|
-
fn call(&mut self, mut req: http::Request<
|
|
223
|
+
fn call(&mut self, mut req: http::Request<Body>) -> Self::Future {
|
|
190
224
|
let metrics = self
|
|
191
225
|
.metrics
|
|
192
226
|
.clone()
|
|
@@ -24,7 +24,7 @@ use temporal_sdk_core_protos::{
|
|
|
24
24
|
};
|
|
25
25
|
use tonic::{
|
|
26
26
|
Request, Response, Status,
|
|
27
|
-
body::
|
|
27
|
+
body::Body,
|
|
28
28
|
client::GrpcService,
|
|
29
29
|
metadata::{AsciiMetadataValue, KeyAndValueRef},
|
|
30
30
|
};
|
|
@@ -166,7 +166,7 @@ where
|
|
|
166
166
|
impl<T> RawClientLike for TemporalServiceClient<T>
|
|
167
167
|
where
|
|
168
168
|
T: Send + Sync + Clone + 'static,
|
|
169
|
-
T: GrpcService<
|
|
169
|
+
T: GrpcService<Body> + Send + Clone + 'static,
|
|
170
170
|
T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
|
|
171
171
|
T::Error: Into<tonic::codegen::StdError>,
|
|
172
172
|
<T::ResponseBody as tonic::codegen::Body>::Error: Into<tonic::codegen::StdError> + Send,
|
|
@@ -221,7 +221,7 @@ where
|
|
|
221
221
|
impl<T> RawClientLike for ConfiguredClient<TemporalServiceClient<T>>
|
|
222
222
|
where
|
|
223
223
|
T: Send + Sync + Clone + 'static,
|
|
224
|
-
T: GrpcService<
|
|
224
|
+
T: GrpcService<Body> + Send + Clone + 'static,
|
|
225
225
|
T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
|
|
226
226
|
T::Error: Into<tonic::codegen::StdError>,
|
|
227
227
|
<T::ResponseBody as tonic::codegen::Body>::Error: Into<tonic::codegen::StdError> + Send,
|
|
@@ -373,7 +373,7 @@ pub(super) struct IsUserLongPoll;
|
|
|
373
373
|
impl<RC, T> WorkflowService for RC
|
|
374
374
|
where
|
|
375
375
|
RC: RawClientLike<SvcType = T>,
|
|
376
|
-
T: GrpcService<
|
|
376
|
+
T: GrpcService<Body> + Send + Clone + 'static,
|
|
377
377
|
T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
|
|
378
378
|
T::Error: Into<tonic::codegen::StdError>,
|
|
379
379
|
T::Future: Send,
|
|
@@ -383,7 +383,7 @@ where
|
|
|
383
383
|
impl<RC, T> OperatorService for RC
|
|
384
384
|
where
|
|
385
385
|
RC: RawClientLike<SvcType = T>,
|
|
386
|
-
T: GrpcService<
|
|
386
|
+
T: GrpcService<Body> + Send + Clone + 'static,
|
|
387
387
|
T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
|
|
388
388
|
T::Error: Into<tonic::codegen::StdError>,
|
|
389
389
|
T::Future: Send,
|
|
@@ -393,7 +393,7 @@ where
|
|
|
393
393
|
impl<RC, T> CloudService for RC
|
|
394
394
|
where
|
|
395
395
|
RC: RawClientLike<SvcType = T>,
|
|
396
|
-
T: GrpcService<
|
|
396
|
+
T: GrpcService<Body> + Send + Clone + 'static,
|
|
397
397
|
T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
|
|
398
398
|
T::Error: Into<tonic::codegen::StdError>,
|
|
399
399
|
T::Future: Send,
|
|
@@ -403,7 +403,7 @@ where
|
|
|
403
403
|
impl<RC, T> TestService for RC
|
|
404
404
|
where
|
|
405
405
|
RC: RawClientLike<SvcType = T>,
|
|
406
|
-
T: GrpcService<
|
|
406
|
+
T: GrpcService<Body> + Send + Clone + 'static,
|
|
407
407
|
T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
|
|
408
408
|
T::Error: Into<tonic::codegen::StdError>,
|
|
409
409
|
T::Future: Send,
|
|
@@ -413,7 +413,7 @@ where
|
|
|
413
413
|
impl<RC, T> HealthService for RC
|
|
414
414
|
where
|
|
415
415
|
RC: RawClientLike<SvcType = T>,
|
|
416
|
-
T: GrpcService<
|
|
416
|
+
T: GrpcService<Body> + Send + Clone + 'static,
|
|
417
417
|
T::ResponseBody: tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
|
|
418
418
|
T::Error: Into<tonic::codegen::StdError>,
|
|
419
419
|
T::Future: Send,
|
|
@@ -437,7 +437,7 @@ macro_rules! proxy {
|
|
|
437
437
|
fn $method(
|
|
438
438
|
&mut self,
|
|
439
439
|
request: impl tonic::IntoRequest<$req>,
|
|
440
|
-
) -> BoxFuture<Result<tonic::Response<$resp>, tonic::Status>> {
|
|
440
|
+
) -> BoxFuture<'_, Result<tonic::Response<$resp>, tonic::Status>> {
|
|
441
441
|
#[allow(unused_mut)]
|
|
442
442
|
let mut as_req = request.into_request();
|
|
443
443
|
$( type_closure_arg(&mut as_req, $closure); )*
|
|
@@ -455,7 +455,7 @@ macro_rules! proxy {
|
|
|
455
455
|
fn $method(
|
|
456
456
|
&mut self,
|
|
457
457
|
request: impl tonic::IntoRequest<$req>,
|
|
458
|
-
) -> BoxFuture<Result<tonic::Response<$resp>, tonic::Status>> {
|
|
458
|
+
) -> BoxFuture<'_, Result<tonic::Response<$resp>, tonic::Status>> {
|
|
459
459
|
#[allow(unused_mut)]
|
|
460
460
|
let mut as_req = request.into_request();
|
|
461
461
|
type_closure_arg(&mut as_req, $closure_request);
|
|
@@ -483,13 +483,13 @@ macro_rules! proxier {
|
|
|
483
483
|
pub trait $trait_name: RawClientLike
|
|
484
484
|
where
|
|
485
485
|
// Yo this is wild
|
|
486
|
-
<Self as RawClientLike>::SvcType: GrpcService<
|
|
487
|
-
<<Self as RawClientLike>::SvcType as GrpcService<
|
|
486
|
+
<Self as RawClientLike>::SvcType: GrpcService<Body> + Send + Clone + 'static,
|
|
487
|
+
<<Self as RawClientLike>::SvcType as GrpcService<Body>>::ResponseBody:
|
|
488
488
|
tonic::codegen::Body<Data = tonic::codegen::Bytes> + Send + 'static,
|
|
489
|
-
<<Self as RawClientLike>::SvcType as GrpcService<
|
|
489
|
+
<<Self as RawClientLike>::SvcType as GrpcService<Body>>::Error:
|
|
490
490
|
Into<tonic::codegen::StdError>,
|
|
491
|
-
<<Self as RawClientLike>::SvcType as GrpcService<
|
|
492
|
-
<<<Self as RawClientLike>::SvcType as GrpcService<
|
|
491
|
+
<<Self as RawClientLike>::SvcType as GrpcService<Body>>::Future: Send,
|
|
492
|
+
<<<Self as RawClientLike>::SvcType as GrpcService<Body>>::ResponseBody
|
|
493
493
|
as tonic::codegen::Body>::Error: Into<tonic::codegen::StdError> + Send,
|
|
494
494
|
{
|
|
495
495
|
$(
|
|
@@ -1336,6 +1336,24 @@ proxier! {
|
|
|
1336
1336
|
r.extensions_mut().insert(labels);
|
|
1337
1337
|
}
|
|
1338
1338
|
);
|
|
1339
|
+
(
|
|
1340
|
+
list_workers,
|
|
1341
|
+
ListWorkersRequest,
|
|
1342
|
+
ListWorkersResponse,
|
|
1343
|
+
|r| {
|
|
1344
|
+
let labels = namespaced_request!(r);
|
|
1345
|
+
r.extensions_mut().insert(labels);
|
|
1346
|
+
}
|
|
1347
|
+
);
|
|
1348
|
+
(
|
|
1349
|
+
record_worker_heartbeat,
|
|
1350
|
+
RecordWorkerHeartbeatRequest,
|
|
1351
|
+
RecordWorkerHeartbeatResponse,
|
|
1352
|
+
|r| {
|
|
1353
|
+
let labels = namespaced_request!(r);
|
|
1354
|
+
r.extensions_mut().insert(labels);
|
|
1355
|
+
}
|
|
1356
|
+
);
|
|
1339
1357
|
}
|
|
1340
1358
|
|
|
1341
1359
|
proxier! {
|
package/sdk-core/core/Cargo.toml
CHANGED
|
@@ -13,9 +13,10 @@ categories = ["development-tools"]
|
|
|
13
13
|
[lib]
|
|
14
14
|
|
|
15
15
|
[features]
|
|
16
|
-
default = ["otel"]
|
|
17
|
-
otel = ["dep:opentelemetry", "dep:opentelemetry_sdk", "dep:opentelemetry-otlp",
|
|
18
|
-
"dep:
|
|
16
|
+
default = ["otel", "prom"]
|
|
17
|
+
otel = ["dep:opentelemetry", "dep:opentelemetry_sdk", "dep:opentelemetry-otlp", "dep:hyper",
|
|
18
|
+
"dep:hyper-util", "dep:http-body-util"]
|
|
19
|
+
prom = ["dep:prometheus"]
|
|
19
20
|
tokio-console = ["console-subscriber"]
|
|
20
21
|
ephemeral-server = ["dep:flate2", "dep:reqwest", "dep:tar", "dep:zip"]
|
|
21
22
|
debug-plugin = ["dep:reqwest"]
|
|
@@ -35,6 +36,7 @@ enum-iterator = "2"
|
|
|
35
36
|
flate2 = { version = "1.0", optional = true }
|
|
36
37
|
futures-util = { version = "0.3", default-features = false }
|
|
37
38
|
futures-channel = { version = "0.3", default-features = false, features = ["std"] }
|
|
39
|
+
gethostname = "1.0.2"
|
|
38
40
|
governor = "0.8"
|
|
39
41
|
http-body-util = { version = "0.1", optional = true }
|
|
40
42
|
hyper = { version = "1.2", optional = true }
|
|
@@ -43,13 +45,12 @@ itertools = "0.14"
|
|
|
43
45
|
lru = "0.13"
|
|
44
46
|
mockall = "0.13"
|
|
45
47
|
opentelemetry = { workspace = true, features = ["metrics"], optional = true }
|
|
46
|
-
opentelemetry_sdk = { version = "0.
|
|
47
|
-
opentelemetry-otlp = { version = "0.
|
|
48
|
-
opentelemetry-prometheus = { version = "0.29", optional = true }
|
|
48
|
+
opentelemetry_sdk = { version = "0.30", features = ["rt-tokio", "metrics", "spec_unstable_metrics_views"], optional = true }
|
|
49
|
+
opentelemetry-otlp = { version = "0.30", features = ["tokio", "metrics", "tls", "http-proto", "grpc-tonic"], optional = true }
|
|
49
50
|
parking_lot = { version = "0.12", features = ["send_guard"] }
|
|
50
51
|
pid = "4.0"
|
|
51
52
|
pin-project = "1.0"
|
|
52
|
-
prometheus = "0.14"
|
|
53
|
+
prometheus = { version = "0.14", optional = true }
|
|
53
54
|
prost = { workspace = true }
|
|
54
55
|
prost-types = { version = "0.6", package = "prost-wkt-types" }
|
|
55
56
|
rand = "0.9"
|
|
@@ -65,7 +66,7 @@ thiserror = { workspace = true }
|
|
|
65
66
|
tokio = { version = "1.37", features = ["rt", "rt-multi-thread", "parking_lot", "time", "fs", "process"] }
|
|
66
67
|
tokio-util = { version = "0.7", features = ["io", "io-util"] }
|
|
67
68
|
tokio-stream = "0.1"
|
|
68
|
-
tonic = { workspace = true, features = ["tls", "tls-roots"] }
|
|
69
|
+
tonic = { workspace = true, features = ["tls-ring", "tls-native-roots"] }
|
|
69
70
|
tracing = "0.1"
|
|
70
71
|
tracing-subscriber = { version = "0.3", default-features = false, features = ["parking_lot", "env-filter", "registry", "ansi"] }
|
|
71
72
|
url = "2.2"
|
|
@@ -91,10 +92,11 @@ path = "../fsm"
|
|
|
91
92
|
assert_matches = "1.4"
|
|
92
93
|
bimap = "0.6.1"
|
|
93
94
|
clap = { version = "4.0", features = ["derive"] }
|
|
94
|
-
criterion = "0.
|
|
95
|
+
criterion = { version = "0.6", features = ["async", "async_tokio"] }
|
|
95
96
|
rstest = "0.25"
|
|
96
97
|
temporal-sdk-core-test-utils = { path = "../test-utils" }
|
|
97
98
|
temporal-sdk = { path = "../sdk" }
|
|
99
|
+
tokio = { version = "1.37", features = ["rt", "rt-multi-thread", "parking_lot", "time", "fs", "process", "test-util"] }
|
|
98
100
|
tokio-stream = { version = "0.1", features = ["net"] }
|
|
99
101
|
|
|
100
102
|
[[test]]
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
use criterion::{Criterion, criterion_group, criterion_main};
|
|
1
|
+
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
|
|
2
2
|
use futures_util::StreamExt;
|
|
3
|
-
use std::
|
|
3
|
+
use std::{
|
|
4
|
+
sync::{Arc, mpsc},
|
|
5
|
+
thread,
|
|
6
|
+
time::Duration,
|
|
7
|
+
};
|
|
4
8
|
use temporal_sdk::{WfContext, WorkflowFunction};
|
|
5
|
-
use temporal_sdk_core::replay::HistoryForReplay;
|
|
9
|
+
use temporal_sdk_core::{CoreRuntime, replay::HistoryForReplay};
|
|
10
|
+
use temporal_sdk_core_api::telemetry::metrics::{
|
|
11
|
+
MetricKeyValue, MetricParametersBuilder, NewAttributes,
|
|
12
|
+
};
|
|
6
13
|
use temporal_sdk_core_protos::DEFAULT_WORKFLOW_TYPE;
|
|
7
|
-
use temporal_sdk_core_test_utils::{
|
|
14
|
+
use temporal_sdk_core_test_utils::{
|
|
15
|
+
DONT_AUTO_INIT_INTEG_TELEM, canned_histories, prom_metrics, replay_sdk_worker,
|
|
16
|
+
};
|
|
8
17
|
|
|
9
18
|
pub fn criterion_benchmark(c: &mut Criterion) {
|
|
10
19
|
let tokio_runtime = tokio::runtime::Builder::new_current_thread()
|
|
@@ -12,6 +21,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|
|
12
21
|
.build()
|
|
13
22
|
.unwrap();
|
|
14
23
|
let _g = tokio_runtime.enter();
|
|
24
|
+
DONT_AUTO_INIT_INTEG_TELEM.set(true);
|
|
15
25
|
|
|
16
26
|
let num_timers = 10;
|
|
17
27
|
let t = canned_histories::long_sequential_timers(num_timers as usize);
|
|
@@ -21,14 +31,17 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|
|
21
31
|
);
|
|
22
32
|
|
|
23
33
|
c.bench_function("Small history replay", |b| {
|
|
24
|
-
b.
|
|
25
|
-
|
|
34
|
+
b.to_async(&tokio_runtime).iter_batched(
|
|
35
|
+
|| {
|
|
26
36
|
let func = timers_wf(num_timers);
|
|
27
|
-
|
|
37
|
+
(func, replay_sdk_worker([hist.clone()]))
|
|
38
|
+
},
|
|
39
|
+
|(func, mut worker)| async move {
|
|
28
40
|
worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
|
|
29
41
|
worker.run().await.unwrap();
|
|
30
|
-
}
|
|
31
|
-
|
|
42
|
+
},
|
|
43
|
+
BatchSize::SmallInput,
|
|
44
|
+
)
|
|
32
45
|
});
|
|
33
46
|
|
|
34
47
|
let num_tasks = 50;
|
|
@@ -39,18 +52,104 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
|
|
39
52
|
);
|
|
40
53
|
|
|
41
54
|
c.bench_function("Large payloads history replay", |b| {
|
|
42
|
-
b.
|
|
43
|
-
|
|
55
|
+
b.to_async(&tokio_runtime).iter_batched(
|
|
56
|
+
|| {
|
|
44
57
|
let func = big_signals_wf(num_tasks);
|
|
45
|
-
|
|
58
|
+
(func, replay_sdk_worker([hist.clone()]))
|
|
59
|
+
},
|
|
60
|
+
|(func, mut worker)| async move {
|
|
46
61
|
worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
|
|
47
62
|
worker.run().await.unwrap();
|
|
48
|
-
}
|
|
49
|
-
|
|
63
|
+
},
|
|
64
|
+
BatchSize::SmallInput,
|
|
65
|
+
)
|
|
50
66
|
});
|
|
51
67
|
}
|
|
52
68
|
|
|
53
|
-
|
|
69
|
+
pub fn bench_metrics(c: &mut Criterion) {
|
|
70
|
+
DONT_AUTO_INIT_INTEG_TELEM.set(true);
|
|
71
|
+
let tokio_runtime = tokio::runtime::Builder::new_current_thread()
|
|
72
|
+
.enable_all()
|
|
73
|
+
.build()
|
|
74
|
+
.unwrap();
|
|
75
|
+
let _tokio = tokio_runtime.enter();
|
|
76
|
+
let (mut telemopts, addr, _aborter) = prom_metrics(None);
|
|
77
|
+
telemopts.logging = None;
|
|
78
|
+
let rt = CoreRuntime::new_assume_tokio(telemopts).unwrap();
|
|
79
|
+
let meter = rt.telemetry().get_metric_meter().unwrap();
|
|
80
|
+
|
|
81
|
+
c.bench_function("Record with new attributes on each call", move |b| {
|
|
82
|
+
b.iter_batched(
|
|
83
|
+
|| {
|
|
84
|
+
let c = meter.counter(
|
|
85
|
+
MetricParametersBuilder::default()
|
|
86
|
+
.name("c")
|
|
87
|
+
.build()
|
|
88
|
+
.unwrap(),
|
|
89
|
+
);
|
|
90
|
+
let h = meter.histogram(
|
|
91
|
+
MetricParametersBuilder::default()
|
|
92
|
+
.name("h")
|
|
93
|
+
.build()
|
|
94
|
+
.unwrap(),
|
|
95
|
+
);
|
|
96
|
+
let g = meter.gauge(
|
|
97
|
+
MetricParametersBuilder::default()
|
|
98
|
+
.name("g")
|
|
99
|
+
.build()
|
|
100
|
+
.unwrap(),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
let vals = [1, 2, 3, 4, 5];
|
|
104
|
+
let labels = ["l1", "l2"];
|
|
105
|
+
|
|
106
|
+
let (start_tx, start_rx) = mpsc::channel();
|
|
107
|
+
let start_rx = Arc::new(std::sync::Mutex::new(start_rx));
|
|
108
|
+
|
|
109
|
+
let mut thread_handles = Vec::new();
|
|
110
|
+
for _ in 0..3 {
|
|
111
|
+
let c = c.clone();
|
|
112
|
+
let h = h.clone();
|
|
113
|
+
let g = g.clone();
|
|
114
|
+
let meter = meter.clone();
|
|
115
|
+
let start_rx = start_rx.clone();
|
|
116
|
+
|
|
117
|
+
let handle = thread::spawn(move || {
|
|
118
|
+
// Wait for start signal
|
|
119
|
+
let _ = start_rx.lock().unwrap().recv();
|
|
120
|
+
|
|
121
|
+
for _ in 1..=100 {
|
|
122
|
+
for &val in &vals {
|
|
123
|
+
for &label in &labels {
|
|
124
|
+
let attribs = meter.new_attributes(NewAttributes::from(vec![
|
|
125
|
+
MetricKeyValue::new("label", label),
|
|
126
|
+
]));
|
|
127
|
+
c.add(val, &attribs);
|
|
128
|
+
h.record(val, &attribs);
|
|
129
|
+
g.record(val, &attribs);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
thread_handles.push(handle);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
(start_tx, thread_handles)
|
|
138
|
+
},
|
|
139
|
+
|(start_tx, thread_handles)| {
|
|
140
|
+
for _ in 0..3 {
|
|
141
|
+
let _ = start_tx.send(());
|
|
142
|
+
}
|
|
143
|
+
for handle in thread_handles {
|
|
144
|
+
let _ = handle.join();
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
BatchSize::SmallInput,
|
|
148
|
+
)
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
criterion_group!(benches, criterion_benchmark, bench_metrics);
|
|
54
153
|
criterion_main!(benches);
|
|
55
154
|
|
|
56
155
|
fn timers_wf(num_timers: u32) -> WorkflowFunction {
|
|
@@ -6,7 +6,7 @@ use crate::{
|
|
|
6
6
|
gen_assert_and_reply, mock_manual_poller, mock_poller, mock_poller_from_resps,
|
|
7
7
|
mock_sdk_cfg, mock_worker, poll_and_reply, single_hist_mock_sg, test_worker_cfg,
|
|
8
8
|
},
|
|
9
|
-
worker::client::mocks::{
|
|
9
|
+
worker::client::mocks::{mock_manual_worker_client, mock_worker_client},
|
|
10
10
|
};
|
|
11
11
|
use futures_util::FutureExt;
|
|
12
12
|
use itertools::Itertools;
|
|
@@ -86,7 +86,7 @@ fn three_tasks() -> VecDeque<PollActivityTaskQueueResponse> {
|
|
|
86
86
|
async fn max_activities_respected() {
|
|
87
87
|
let _task_q = "q";
|
|
88
88
|
let mut tasks = three_tasks();
|
|
89
|
-
let mut mock_client =
|
|
89
|
+
let mut mock_client = mock_worker_client();
|
|
90
90
|
mock_client
|
|
91
91
|
.expect_poll_activity_task()
|
|
92
92
|
.times(3)
|
|
@@ -122,7 +122,7 @@ async fn max_activities_respected() {
|
|
|
122
122
|
|
|
123
123
|
#[tokio::test]
|
|
124
124
|
async fn activity_not_found_returns_ok() {
|
|
125
|
-
let mut mock_client =
|
|
125
|
+
let mut mock_client = mock_worker_client();
|
|
126
126
|
// Mock won't even be called, since we weren't tracking activity
|
|
127
127
|
mock_client.expect_complete_activity_task().times(0);
|
|
128
128
|
|
|
@@ -139,7 +139,7 @@ async fn activity_not_found_returns_ok() {
|
|
|
139
139
|
|
|
140
140
|
#[tokio::test]
|
|
141
141
|
async fn heartbeats_report_cancels_only_once() {
|
|
142
|
-
let mut mock_client =
|
|
142
|
+
let mut mock_client = mock_worker_client();
|
|
143
143
|
mock_client
|
|
144
144
|
.expect_record_activity_heartbeat()
|
|
145
145
|
.times(2)
|
|
@@ -265,7 +265,7 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
265
265
|
.times(3)
|
|
266
266
|
.returning(move || poll_resps.pop_front().unwrap());
|
|
267
267
|
|
|
268
|
-
let mut mock_client =
|
|
268
|
+
let mut mock_client = mock_manual_worker_client();
|
|
269
269
|
mock_client
|
|
270
270
|
.expect_record_activity_heartbeat()
|
|
271
271
|
.times(1)
|
|
@@ -323,7 +323,7 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
323
323
|
|
|
324
324
|
#[tokio::test]
|
|
325
325
|
async fn activity_poll_timeout_retries() {
|
|
326
|
-
let mock_client =
|
|
326
|
+
let mock_client = mock_worker_client();
|
|
327
327
|
let mut calls = 0;
|
|
328
328
|
let mut mock_act_poller = mock_poller();
|
|
329
329
|
mock_act_poller.expect_poll().times(3).returning(move || {
|
|
@@ -352,7 +352,7 @@ async fn many_concurrent_heartbeat_cancels() {
|
|
|
352
352
|
// them after a few successful heartbeats
|
|
353
353
|
const CONCURRENCY_NUM: usize = 5;
|
|
354
354
|
|
|
355
|
-
let mut mock_client =
|
|
355
|
+
let mut mock_client = mock_manual_worker_client();
|
|
356
356
|
let mut poll_resps = VecDeque::from(
|
|
357
357
|
(0..CONCURRENCY_NUM)
|
|
358
358
|
.map(|i| {
|
|
@@ -516,7 +516,7 @@ async fn activity_timeout_no_double_resolve() {
|
|
|
516
516
|
|
|
517
517
|
#[tokio::test]
|
|
518
518
|
async fn can_heartbeat_acts_during_shutdown() {
|
|
519
|
-
let mut mock_client =
|
|
519
|
+
let mut mock_client = mock_worker_client();
|
|
520
520
|
mock_client
|
|
521
521
|
.expect_record_activity_heartbeat()
|
|
522
522
|
.times(1)
|
|
@@ -567,7 +567,7 @@ async fn can_heartbeat_acts_during_shutdown() {
|
|
|
567
567
|
#[tokio::test]
|
|
568
568
|
async fn complete_act_with_fail_flushes_heartbeat() {
|
|
569
569
|
let last_hb = 50;
|
|
570
|
-
let mut mock_client =
|
|
570
|
+
let mut mock_client = mock_worker_client();
|
|
571
571
|
let last_seen_payload = Rc::new(RefCell::new(None));
|
|
572
572
|
let lsp = last_seen_payload.clone();
|
|
573
573
|
mock_client
|
|
@@ -622,7 +622,7 @@ async fn complete_act_with_fail_flushes_heartbeat() {
|
|
|
622
622
|
#[tokio::test]
|
|
623
623
|
async fn max_tq_acts_set_passed_to_poll_properly() {
|
|
624
624
|
let rate = 9.28;
|
|
625
|
-
let mut mock_client =
|
|
625
|
+
let mut mock_client = mock_worker_client();
|
|
626
626
|
mock_client
|
|
627
627
|
.expect_poll_activity_task()
|
|
628
628
|
.returning(move |_, ao| {
|
|
@@ -659,7 +659,7 @@ async fn no_eager_activities_requested_when_worker_options_disable_it(
|
|
|
659
659
|
let num_eager_requested = Arc::new(AtomicUsize::new(0));
|
|
660
660
|
let num_eager_requested_clone = num_eager_requested.clone();
|
|
661
661
|
|
|
662
|
-
let mut mock =
|
|
662
|
+
let mut mock = mock_worker_client();
|
|
663
663
|
mock.expect_complete_workflow_task()
|
|
664
664
|
.times(1)
|
|
665
665
|
.returning(move |req| {
|
|
@@ -747,7 +747,7 @@ async fn activity_tasks_from_completion_are_delivered() {
|
|
|
747
747
|
// Clone it to move into the callback below
|
|
748
748
|
let num_eager_requested_clone = num_eager_requested.clone();
|
|
749
749
|
|
|
750
|
-
let mut mock =
|
|
750
|
+
let mut mock = mock_worker_client();
|
|
751
751
|
mock.expect_complete_workflow_task()
|
|
752
752
|
.times(1)
|
|
753
753
|
.returning(move |req| {
|
|
@@ -876,7 +876,7 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
876
876
|
t.add_full_wf_task();
|
|
877
877
|
t.add_workflow_execution_completed();
|
|
878
878
|
|
|
879
|
-
let mut mock =
|
|
879
|
+
let mut mock = mock_worker_client();
|
|
880
880
|
// Set up two tasks to be returned via normal activity polling
|
|
881
881
|
let act_tasks = VecDeque::from(vec![
|
|
882
882
|
PollActivityTaskQueueResponse {
|
|
@@ -1004,7 +1004,7 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
1004
1004
|
|
|
1005
1005
|
#[tokio::test]
|
|
1006
1006
|
async fn retryable_net_error_exhaustion_is_nonfatal() {
|
|
1007
|
-
let mut mock_client =
|
|
1007
|
+
let mut mock_client = mock_worker_client();
|
|
1008
1008
|
mock_client
|
|
1009
1009
|
.expect_complete_activity_task()
|
|
1010
1010
|
.times(1)
|
|
@@ -1033,7 +1033,7 @@ async fn retryable_net_error_exhaustion_is_nonfatal() {
|
|
|
1033
1033
|
|
|
1034
1034
|
#[tokio::test]
|
|
1035
1035
|
async fn cant_complete_activity_with_unset_result_payload() {
|
|
1036
|
-
let mut mock_client =
|
|
1036
|
+
let mut mock_client = mock_worker_client();
|
|
1037
1037
|
mock_client
|
|
1038
1038
|
.expect_poll_activity_task()
|
|
1039
1039
|
.returning(move |_, _| {
|
|
@@ -1076,7 +1076,7 @@ async fn graceful_shutdown(#[values(true, false)] at_max_outstanding: bool) {
|
|
|
1076
1076
|
.times(1)
|
|
1077
1077
|
.returning(move || None);
|
|
1078
1078
|
// They shall all be reported as failed
|
|
1079
|
-
let mut mock_client =
|
|
1079
|
+
let mut mock_client = mock_worker_client();
|
|
1080
1080
|
mock_client
|
|
1081
1081
|
.expect_fail_activity_task()
|
|
1082
1082
|
.times(3)
|
|
@@ -1153,7 +1153,7 @@ async fn activities_must_be_flushed_to_server_on_shutdown(#[values(true, false)]
|
|
|
1153
1153
|
.expect_poll()
|
|
1154
1154
|
.times(1)
|
|
1155
1155
|
.returning(move || None);
|
|
1156
|
-
let mut mock_client =
|
|
1156
|
+
let mut mock_client = mock_manual_worker_client();
|
|
1157
1157
|
mock_client
|
|
1158
1158
|
.expect_complete_activity_task()
|
|
1159
1159
|
.times(1)
|
|
@@ -1251,7 +1251,7 @@ async fn pass_activity_summary_to_metadata() {
|
|
|
1251
1251
|
|
|
1252
1252
|
#[tokio::test]
|
|
1253
1253
|
async fn heartbeat_response_can_be_paused() {
|
|
1254
|
-
let mut mock_client =
|
|
1254
|
+
let mut mock_client = mock_worker_client();
|
|
1255
1255
|
// First heartbeat returns pause only
|
|
1256
1256
|
mock_client
|
|
1257
1257
|
.expect_record_activity_heartbeat()
|