@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,14 +1,48 @@
|
|
|
1
|
+
use crate::telemetry::prometheus_meter::Registry;
|
|
1
2
|
use http_body_util::Full;
|
|
2
3
|
use hyper::{Method, Request, Response, body::Bytes, header::CONTENT_TYPE, service::service_fn};
|
|
3
4
|
use hyper_util::{
|
|
4
5
|
rt::{TokioExecutor, TokioIo},
|
|
5
6
|
server::conn::auto,
|
|
6
7
|
};
|
|
7
|
-
use
|
|
8
|
-
use
|
|
9
|
-
|
|
8
|
+
use prometheus::{Encoder, TextEncoder};
|
|
9
|
+
use std::{
|
|
10
|
+
net::{SocketAddr, TcpListener},
|
|
11
|
+
sync::Arc,
|
|
12
|
+
};
|
|
10
13
|
use temporal_sdk_core_api::telemetry::PrometheusExporterOptions;
|
|
11
|
-
use tokio::io;
|
|
14
|
+
use tokio::{io, task::AbortHandle};
|
|
15
|
+
|
|
16
|
+
pub struct StartedPromServer {
|
|
17
|
+
pub meter: Arc<crate::telemetry::prometheus_meter::CorePrometheusMeter>,
|
|
18
|
+
pub bound_addr: SocketAddr,
|
|
19
|
+
pub abort_handle: AbortHandle,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Builds and runs a prometheus endpoint which can be scraped by prom instances for metrics export.
|
|
23
|
+
/// Returns the meter that can be used as a [CoreMeter].
|
|
24
|
+
///
|
|
25
|
+
/// Requires a Tokio runtime to exist, and will block briefly while binding the server endpoint.
|
|
26
|
+
pub fn start_prometheus_metric_exporter(
|
|
27
|
+
opts: PrometheusExporterOptions,
|
|
28
|
+
) -> Result<StartedPromServer, anyhow::Error> {
|
|
29
|
+
let srv = PromServer::new(&opts)?;
|
|
30
|
+
let meter = Arc::new(
|
|
31
|
+
crate::telemetry::prometheus_meter::CorePrometheusMeter::new(
|
|
32
|
+
srv.registry().clone(),
|
|
33
|
+
opts.use_seconds_for_durations,
|
|
34
|
+
opts.unit_suffix,
|
|
35
|
+
opts.histogram_bucket_overrides,
|
|
36
|
+
),
|
|
37
|
+
);
|
|
38
|
+
let bound_addr = srv.bound_addr()?;
|
|
39
|
+
let handle = tokio::spawn(async move { srv.run().await });
|
|
40
|
+
Ok(StartedPromServer {
|
|
41
|
+
meter,
|
|
42
|
+
bound_addr,
|
|
43
|
+
abort_handle: handle.abort_handle(),
|
|
44
|
+
})
|
|
45
|
+
}
|
|
12
46
|
|
|
13
47
|
/// Exposes prometheus metrics for scraping
|
|
14
48
|
pub(super) struct PromServer {
|
|
@@ -17,30 +51,16 @@ pub(super) struct PromServer {
|
|
|
17
51
|
}
|
|
18
52
|
|
|
19
53
|
impl PromServer {
|
|
20
|
-
pub(super) fn new(
|
|
21
|
-
opts
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
exporter
|
|
31
|
-
};
|
|
32
|
-
let exporter = if !opts.unit_suffix {
|
|
33
|
-
exporter.without_units()
|
|
34
|
-
} else {
|
|
35
|
-
exporter
|
|
36
|
-
};
|
|
37
|
-
Ok((
|
|
38
|
-
Self {
|
|
39
|
-
listener: TcpListener::bind(opts.socket_addr)?,
|
|
40
|
-
registry,
|
|
41
|
-
},
|
|
42
|
-
exporter.build()?,
|
|
43
|
-
))
|
|
54
|
+
pub(super) fn new(opts: &PrometheusExporterOptions) -> Result<Self, anyhow::Error> {
|
|
55
|
+
let registry = Registry::new(opts.global_tags.clone());
|
|
56
|
+
Ok(Self {
|
|
57
|
+
listener: TcpListener::bind(opts.socket_addr)?,
|
|
58
|
+
registry,
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub(super) fn registry(&self) -> &Registry {
|
|
63
|
+
&self.registry
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
pub(super) async fn run(self) -> Result<(), anyhow::Error> {
|
|
@@ -9,7 +9,8 @@ use crate::{
|
|
|
9
9
|
worker::{
|
|
10
10
|
TaskPollers,
|
|
11
11
|
client::{
|
|
12
|
-
MockWorkerClient, WorkerClient, WorkflowTaskCompletion,
|
|
12
|
+
LegacyQueryResult, MockWorkerClient, WorkerClient, WorkflowTaskCompletion,
|
|
13
|
+
mocks::mock_worker_client,
|
|
13
14
|
},
|
|
14
15
|
},
|
|
15
16
|
};
|
|
@@ -179,6 +180,7 @@ pub(crate) fn mock_worker(mocks: MocksHolder) -> Worker {
|
|
|
179
180
|
.unwrap_or_else(|| mock_poller_from_resps([])),
|
|
180
181
|
},
|
|
181
182
|
None,
|
|
183
|
+
None,
|
|
182
184
|
)
|
|
183
185
|
}
|
|
184
186
|
|
|
@@ -406,6 +408,9 @@ pub(crate) struct MockPollCfg {
|
|
|
406
408
|
/// All calls to fail WFTs must match this predicate
|
|
407
409
|
pub(crate) expect_fail_wft_matcher:
|
|
408
410
|
Box<dyn Fn(&TaskToken, &WorkflowTaskFailedCause, &Option<Failure>) -> bool + Send>,
|
|
411
|
+
/// All calls to legacy query responses must match this predicate
|
|
412
|
+
pub(crate) expect_legacy_query_matcher:
|
|
413
|
+
Box<dyn Fn(&TaskToken, &LegacyQueryResult) -> bool + Send>,
|
|
409
414
|
pub(crate) completion_mock_fn: Option<Box<WFTCompletionMockFn>>,
|
|
410
415
|
pub(crate) num_expected_completions: Option<TimesRange>,
|
|
411
416
|
/// If being used with the Rust SDK, this is set true. It ensures pollers will not error out
|
|
@@ -426,8 +431,9 @@ impl MockPollCfg {
|
|
|
426
431
|
enforce_correct_number_of_polls,
|
|
427
432
|
num_expected_fails,
|
|
428
433
|
num_expected_legacy_query_resps: 0,
|
|
429
|
-
mock_client:
|
|
434
|
+
mock_client: mock_worker_client(),
|
|
430
435
|
expect_fail_wft_matcher: Box::new(|_, _, _| true),
|
|
436
|
+
expect_legacy_query_matcher: Box::new(|_, _| true),
|
|
431
437
|
completion_mock_fn: None,
|
|
432
438
|
num_expected_completions: None,
|
|
433
439
|
using_rust_sdk: false,
|
|
@@ -439,14 +445,14 @@ impl MockPollCfg {
|
|
|
439
445
|
pub(crate) fn from_hist_builder(t: TestHistoryBuilder) -> Self {
|
|
440
446
|
let full_hist_info = t.get_full_history_info().unwrap();
|
|
441
447
|
let tasks = 1..=full_hist_info.wf_task_count();
|
|
442
|
-
Self::from_resp_batches("fake_wf_id", t, tasks,
|
|
448
|
+
Self::from_resp_batches("fake_wf_id", t, tasks, mock_worker_client())
|
|
443
449
|
}
|
|
444
450
|
|
|
445
451
|
pub(crate) fn from_resps(
|
|
446
452
|
t: TestHistoryBuilder,
|
|
447
453
|
resps: impl IntoIterator<Item = impl Into<ResponseType>>,
|
|
448
454
|
) -> Self {
|
|
449
|
-
Self::from_resp_batches("fake_wf_id", t, resps,
|
|
455
|
+
Self::from_resp_batches("fake_wf_id", t, resps, mock_worker_client())
|
|
450
456
|
}
|
|
451
457
|
|
|
452
458
|
pub(crate) fn from_resp_batches(
|
|
@@ -466,6 +472,7 @@ impl MockPollCfg {
|
|
|
466
472
|
num_expected_legacy_query_resps: 0,
|
|
467
473
|
mock_client,
|
|
468
474
|
expect_fail_wft_matcher: Box::new(|_, _, _| true),
|
|
475
|
+
expect_legacy_query_matcher: Box::new(|_, _| true),
|
|
469
476
|
completion_mock_fn: None,
|
|
470
477
|
num_expected_completions: None,
|
|
471
478
|
using_rust_sdk: false,
|
|
@@ -709,6 +716,7 @@ pub(crate) fn build_mock_pollers(mut cfg: MockPollCfg) -> MocksHolder {
|
|
|
709
716
|
let outstanding = outstanding_wf_task_tokens.clone();
|
|
710
717
|
cfg.mock_client
|
|
711
718
|
.expect_respond_legacy_query()
|
|
719
|
+
.withf(cfg.expect_legacy_query_matcher)
|
|
712
720
|
.times::<TimesRange>(cfg.num_expected_legacy_query_resps.into())
|
|
713
721
|
.returning(move |tt, _| {
|
|
714
722
|
outstanding.release_token(&tt);
|
|
@@ -948,10 +956,8 @@ pub(crate) async fn poll_and_reply_clears_outstanding_evicts<'a>(
|
|
|
948
956
|
|
|
949
957
|
worker.complete_workflow_activation(reply).await.unwrap();
|
|
950
958
|
|
|
951
|
-
if do_release {
|
|
952
|
-
|
|
953
|
-
omap.release_run(&res.run_id);
|
|
954
|
-
}
|
|
959
|
+
if do_release && let Some(omap) = outstanding_map.as_ref() {
|
|
960
|
+
omap.release_run(&res.run_id);
|
|
955
961
|
}
|
|
956
962
|
// Restart assertions from the beginning if it was an eviction (and workflow execution
|
|
957
963
|
// isn't over)
|
|
@@ -1068,10 +1074,19 @@ impl WorkerExt for Worker {
|
|
|
1068
1074
|
);
|
|
1069
1075
|
},
|
|
1070
1076
|
async {
|
|
1071
|
-
|
|
1072
|
-
self.poll_workflow_activation().await
|
|
1073
|
-
|
|
1074
|
-
|
|
1077
|
+
loop {
|
|
1078
|
+
match self.poll_workflow_activation().await {
|
|
1079
|
+
Err(PollError::ShutDown) => break,
|
|
1080
|
+
Ok(a) if a.is_only_eviction() => {
|
|
1081
|
+
self.complete_workflow_activation(WorkflowActivationCompletion::empty(
|
|
1082
|
+
a.run_id,
|
|
1083
|
+
))
|
|
1084
|
+
.await
|
|
1085
|
+
.unwrap();
|
|
1086
|
+
}
|
|
1087
|
+
o => panic!("Unexpected activation while draining: {o:?}"),
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1075
1090
|
}
|
|
1076
1091
|
);
|
|
1077
1092
|
self.finalize_shutdown().await;
|
|
@@ -423,7 +423,7 @@ impl HeartbeatStreamState {
|
|
|
423
423
|
mod test {
|
|
424
424
|
use super::*;
|
|
425
425
|
|
|
426
|
-
use crate::worker::client::mocks::
|
|
426
|
+
use crate::worker::client::mocks::mock_worker_client;
|
|
427
427
|
use std::time::Duration;
|
|
428
428
|
use temporal_sdk_core_protos::temporal::api::{
|
|
429
429
|
common::v1::Payload, workflowservice::v1::RecordActivityTaskHeartbeatResponse,
|
|
@@ -434,7 +434,7 @@ mod test {
|
|
|
434
434
|
/// every 1/2 of the heartbeat timeout.
|
|
435
435
|
#[tokio::test]
|
|
436
436
|
async fn process_heartbeats_and_shutdown() {
|
|
437
|
-
let mut mock_client =
|
|
437
|
+
let mut mock_client = mock_worker_client();
|
|
438
438
|
mock_client
|
|
439
439
|
.expect_record_activity_heartbeat()
|
|
440
440
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
@@ -456,7 +456,7 @@ mod test {
|
|
|
456
456
|
|
|
457
457
|
#[tokio::test]
|
|
458
458
|
async fn send_heartbeats_less_frequently_than_throttle_interval() {
|
|
459
|
-
let mut mock_client =
|
|
459
|
+
let mut mock_client = mock_worker_client();
|
|
460
460
|
mock_client
|
|
461
461
|
.expect_record_activity_heartbeat()
|
|
462
462
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
@@ -475,7 +475,7 @@ mod test {
|
|
|
475
475
|
/// Ensure that heartbeat can be called from a tight loop and correctly throttle
|
|
476
476
|
#[tokio::test]
|
|
477
477
|
async fn process_tight_loop_and_shutdown() {
|
|
478
|
-
let mut mock_client =
|
|
478
|
+
let mut mock_client = mock_worker_client();
|
|
479
479
|
mock_client
|
|
480
480
|
.expect_record_activity_heartbeat()
|
|
481
481
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
@@ -495,7 +495,7 @@ mod test {
|
|
|
495
495
|
/// This test reports one heartbeat and waits for the throttle_interval to elapse before sending another
|
|
496
496
|
#[tokio::test]
|
|
497
497
|
async fn report_heartbeat_after_timeout() {
|
|
498
|
-
let mut mock_client =
|
|
498
|
+
let mut mock_client = mock_worker_client();
|
|
499
499
|
mock_client
|
|
500
500
|
.expect_record_activity_heartbeat()
|
|
501
501
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
@@ -513,7 +513,7 @@ mod test {
|
|
|
513
513
|
|
|
514
514
|
#[tokio::test]
|
|
515
515
|
async fn evict_works() {
|
|
516
|
-
let mut mock_client =
|
|
516
|
+
let mut mock_client = mock_worker_client();
|
|
517
517
|
mock_client
|
|
518
518
|
.expect_record_activity_heartbeat()
|
|
519
519
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
@@ -534,7 +534,7 @@ mod test {
|
|
|
534
534
|
|
|
535
535
|
#[tokio::test]
|
|
536
536
|
async fn evict_immediate_after_record() {
|
|
537
|
-
let mut mock_client =
|
|
537
|
+
let mut mock_client = mock_worker_client();
|
|
538
538
|
mock_client
|
|
539
539
|
.expect_record_activity_heartbeat()
|
|
540
540
|
.returning(|_, _| Ok(RecordActivityTaskHeartbeatResponse::default()))
|
|
@@ -726,14 +726,14 @@ mod tests {
|
|
|
726
726
|
abstractions::tests::fixed_size_permit_dealer,
|
|
727
727
|
pollers::{ActivityTaskOptions, LongPollBuffer},
|
|
728
728
|
prost_dur,
|
|
729
|
-
worker::client::mocks::
|
|
729
|
+
worker::client::mocks::mock_worker_client,
|
|
730
730
|
};
|
|
731
731
|
use temporal_sdk_core_api::worker::PollerBehavior;
|
|
732
732
|
use temporal_sdk_core_protos::coresdk::activity_result::ActivityExecutionResult;
|
|
733
733
|
|
|
734
734
|
#[tokio::test]
|
|
735
735
|
async fn per_worker_ratelimit() {
|
|
736
|
-
let mut mock_client =
|
|
736
|
+
let mut mock_client = mock_worker_client();
|
|
737
737
|
mock_client
|
|
738
738
|
.expect_poll_activity_task()
|
|
739
739
|
.times(1)
|
|
@@ -812,7 +812,7 @@ mod tests {
|
|
|
812
812
|
|
|
813
813
|
#[tokio::test]
|
|
814
814
|
async fn local_timeouts() {
|
|
815
|
-
let mut mock_client =
|
|
815
|
+
let mut mock_client = mock_worker_client();
|
|
816
816
|
mock_client
|
|
817
817
|
.expect_poll_activity_task()
|
|
818
818
|
.times(1)
|
|
@@ -902,7 +902,7 @@ mod tests {
|
|
|
902
902
|
|
|
903
903
|
#[tokio::test]
|
|
904
904
|
async fn local_timeout_heartbeating() {
|
|
905
|
-
let mut mock_client =
|
|
905
|
+
let mut mock_client = mock_worker_client();
|
|
906
906
|
mock_client
|
|
907
907
|
.expect_poll_activity_task()
|
|
908
908
|
.times(1)
|
|
@@ -22,7 +22,7 @@ pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
|
|
|
22
22
|
|
|
23
23
|
#[cfg(test)]
|
|
24
24
|
/// Create a mock client primed with basic necessary expectations
|
|
25
|
-
pub(crate) fn
|
|
25
|
+
pub(crate) fn mock_worker_client() -> MockWorkerClient {
|
|
26
26
|
let mut r = MockWorkerClient::new();
|
|
27
27
|
r.expect_capabilities()
|
|
28
28
|
.returning(|| Some(*DEFAULT_TEST_CAPABILITIES));
|
|
@@ -33,11 +33,13 @@ pub(crate) fn mock_workflow_client() -> MockWorkerClient {
|
|
|
33
33
|
.returning(|_| Ok(ShutdownWorkerResponse {}));
|
|
34
34
|
r.expect_sdk_name_and_version()
|
|
35
35
|
.returning(|| ("test-core".to_string(), "0.0.0".to_string()));
|
|
36
|
+
r.expect_get_identity()
|
|
37
|
+
.returning(|| "test-identity".to_string());
|
|
36
38
|
r
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/// Create a mock manual client primed with basic necessary expectations
|
|
40
|
-
pub(crate) fn
|
|
42
|
+
pub(crate) fn mock_manual_worker_client() -> MockManualWorkerClient {
|
|
41
43
|
let mut r = MockManualWorkerClient::new();
|
|
42
44
|
r.expect_capabilities()
|
|
43
45
|
.returning(|| Some(*DEFAULT_TEST_CAPABILITIES));
|
|
@@ -46,6 +48,8 @@ pub(crate) fn mock_manual_workflow_client() -> MockManualWorkerClient {
|
|
|
46
48
|
r.expect_is_mock().returning(|| true);
|
|
47
49
|
r.expect_sdk_name_and_version()
|
|
48
50
|
.returning(|| ("test-core".to_string(), "0.0.0".to_string()));
|
|
51
|
+
r.expect_get_identity()
|
|
52
|
+
.returning(|| "test-identity".to_string());
|
|
49
53
|
r
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -135,7 +139,7 @@ mockall::mock! {
|
|
|
135
139
|
fn respond_legacy_query<'a, 'b>(
|
|
136
140
|
&self,
|
|
137
141
|
task_token: TaskToken,
|
|
138
|
-
|
|
142
|
+
query_result: LegacyQueryResult,
|
|
139
143
|
) -> impl Future<Output = Result<RespondQueryTaskCompletedResponse>> + Send + 'b
|
|
140
144
|
where 'a: 'b, Self: 'b;
|
|
141
145
|
|
|
@@ -146,10 +150,13 @@ mockall::mock! {
|
|
|
146
150
|
fn shutdown_worker<'a, 'b>(&self, sticky_task_queue: String) -> impl Future<Output = Result<ShutdownWorkerResponse>> + Send + 'b
|
|
147
151
|
where 'a: 'b, Self: 'b;
|
|
148
152
|
|
|
153
|
+
fn record_worker_heartbeat<'a, 'b>(&self, heartbeat: WorkerHeartbeat) -> impl Future<Output = Result<RecordWorkerHeartbeatResponse>> + Send + 'b where 'a: 'b, Self: 'b;
|
|
154
|
+
|
|
149
155
|
fn replace_client(&self, new_client: RetryClient<Client>);
|
|
150
156
|
fn capabilities(&self) -> Option<Capabilities>;
|
|
151
157
|
fn workers(&self) -> Arc<SlotManager>;
|
|
152
158
|
fn is_mock(&self) -> bool;
|
|
153
159
|
fn sdk_name_and_version(&self) -> (String, String);
|
|
160
|
+
fn get_identity(&self) -> String;
|
|
154
161
|
}
|
|
155
162
|
}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
//! Worker-specific client needs
|
|
2
2
|
|
|
3
3
|
pub(crate) mod mocks;
|
|
4
|
+
use crate::abstractions::dbg_panic;
|
|
5
|
+
use crate::protosext::legacy_query_failure;
|
|
6
|
+
use crate::worker::heartbeat::HeartbeatFn;
|
|
4
7
|
use parking_lot::RwLock;
|
|
8
|
+
use std::sync::OnceLock;
|
|
5
9
|
use std::{sync::Arc, time::Duration};
|
|
6
10
|
use temporal_client::{
|
|
7
11
|
Client, IsWorkerTaskLongPoll, Namespace, NamespacedClient, NoRetryOnMatching, RetryClient,
|
|
8
12
|
SlotManager, WorkflowService,
|
|
9
13
|
};
|
|
10
14
|
use temporal_sdk_core_api::worker::WorkerVersioningStrategy;
|
|
15
|
+
use temporal_sdk_core_protos::temporal::api::worker::v1::WorkerHeartbeat;
|
|
11
16
|
use temporal_sdk_core_protos::{
|
|
12
17
|
TaskToken,
|
|
13
|
-
coresdk::workflow_commands::QueryResult,
|
|
18
|
+
coresdk::{workflow_commands::QueryResult, workflow_completion},
|
|
14
19
|
temporal::api::{
|
|
15
20
|
command::v1::Command,
|
|
16
21
|
common::v1::{
|
|
@@ -34,12 +39,18 @@ use tonic::IntoRequest;
|
|
|
34
39
|
|
|
35
40
|
type Result<T, E = tonic::Status> = std::result::Result<T, E>;
|
|
36
41
|
|
|
42
|
+
pub enum LegacyQueryResult {
|
|
43
|
+
Succeeded(QueryResult),
|
|
44
|
+
Failed(workflow_completion::Failure),
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
/// Contains everything a worker needs to interact with the server
|
|
38
48
|
pub(crate) struct WorkerClientBag {
|
|
39
49
|
replaceable_client: RwLock<RetryClient<Client>>,
|
|
40
50
|
namespace: String,
|
|
41
51
|
identity: String,
|
|
42
52
|
worker_versioning_strategy: WorkerVersioningStrategy,
|
|
53
|
+
heartbeat_data: Arc<OnceLock<HeartbeatFn>>,
|
|
43
54
|
}
|
|
44
55
|
|
|
45
56
|
impl WorkerClientBag {
|
|
@@ -48,12 +59,14 @@ impl WorkerClientBag {
|
|
|
48
59
|
namespace: String,
|
|
49
60
|
identity: String,
|
|
50
61
|
worker_versioning_strategy: WorkerVersioningStrategy,
|
|
62
|
+
heartbeat_data: Arc<OnceLock<HeartbeatFn>>,
|
|
51
63
|
) -> Self {
|
|
52
64
|
Self {
|
|
53
65
|
replaceable_client: RwLock::new(client),
|
|
54
66
|
namespace,
|
|
55
67
|
identity,
|
|
56
68
|
worker_versioning_strategy,
|
|
69
|
+
heartbeat_data,
|
|
57
70
|
}
|
|
58
71
|
}
|
|
59
72
|
|
|
@@ -114,6 +127,15 @@ impl WorkerClientBag {
|
|
|
114
127
|
None
|
|
115
128
|
}
|
|
116
129
|
}
|
|
130
|
+
|
|
131
|
+
fn capture_heartbeat(&self) -> Option<WorkerHeartbeat> {
|
|
132
|
+
if let Some(hb) = self.heartbeat_data.get() {
|
|
133
|
+
hb()
|
|
134
|
+
} else {
|
|
135
|
+
dbg_panic!("Heartbeat function never set");
|
|
136
|
+
None
|
|
137
|
+
}
|
|
138
|
+
}
|
|
117
139
|
}
|
|
118
140
|
|
|
119
141
|
/// This trait contains everything workers need to interact with Temporal, and hence provides a
|
|
@@ -197,12 +219,17 @@ pub trait WorkerClient: Sync + Send {
|
|
|
197
219
|
async fn respond_legacy_query(
|
|
198
220
|
&self,
|
|
199
221
|
task_token: TaskToken,
|
|
200
|
-
query_result:
|
|
222
|
+
query_result: LegacyQueryResult,
|
|
201
223
|
) -> Result<RespondQueryTaskCompletedResponse>;
|
|
202
224
|
/// Describe the namespace
|
|
203
225
|
async fn describe_namespace(&self) -> Result<DescribeNamespaceResponse>;
|
|
204
226
|
/// Shutdown the worker
|
|
205
227
|
async fn shutdown_worker(&self, sticky_task_queue: String) -> Result<ShutdownWorkerResponse>;
|
|
228
|
+
/// Record a worker heartbeat
|
|
229
|
+
async fn record_worker_heartbeat(
|
|
230
|
+
&self,
|
|
231
|
+
heartbeat: WorkerHeartbeat,
|
|
232
|
+
) -> Result<RecordWorkerHeartbeatResponse>;
|
|
206
233
|
|
|
207
234
|
/// Replace the underlying client
|
|
208
235
|
fn replace_client(&self, new_client: RetryClient<Client>);
|
|
@@ -214,6 +241,8 @@ pub trait WorkerClient: Sync + Send {
|
|
|
214
241
|
fn is_mock(&self) -> bool;
|
|
215
242
|
/// Return name and version of the SDK
|
|
216
243
|
fn sdk_name_and_version(&self) -> (String, String);
|
|
244
|
+
/// Get worker identity
|
|
245
|
+
fn get_identity(&self) -> String;
|
|
217
246
|
}
|
|
218
247
|
|
|
219
248
|
/// Configuration options shared by workflow, activity, and Nexus polling calls
|
|
@@ -267,6 +296,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
267
296
|
binary_checksum: self.binary_checksum(),
|
|
268
297
|
worker_version_capabilities: self.worker_version_capabilities(),
|
|
269
298
|
deployment_options: self.deployment_options(),
|
|
299
|
+
worker_heartbeat: None,
|
|
270
300
|
}
|
|
271
301
|
.into_request();
|
|
272
302
|
request.extensions_mut().insert(IsWorkerTaskLongPoll);
|
|
@@ -303,6 +333,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
303
333
|
}),
|
|
304
334
|
worker_version_capabilities: self.worker_version_capabilities(),
|
|
305
335
|
deployment_options: self.deployment_options(),
|
|
336
|
+
worker_heartbeat: None,
|
|
306
337
|
}
|
|
307
338
|
.into_request();
|
|
308
339
|
request.extensions_mut().insert(IsWorkerTaskLongPoll);
|
|
@@ -335,6 +366,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
335
366
|
identity: self.identity.clone(),
|
|
336
367
|
worker_version_capabilities: self.worker_version_capabilities(),
|
|
337
368
|
deployment_options: self.deployment_options(),
|
|
369
|
+
worker_heartbeat: self.capture_heartbeat().into_iter().collect(),
|
|
338
370
|
}
|
|
339
371
|
.into_request();
|
|
340
372
|
request.extensions_mut().insert(IsWorkerTaskLongPoll);
|
|
@@ -578,9 +610,20 @@ impl WorkerClient for WorkerClientBag {
|
|
|
578
610
|
async fn respond_legacy_query(
|
|
579
611
|
&self,
|
|
580
612
|
task_token: TaskToken,
|
|
581
|
-
query_result:
|
|
613
|
+
query_result: LegacyQueryResult,
|
|
582
614
|
) -> Result<RespondQueryTaskCompletedResponse> {
|
|
615
|
+
let mut failure = None;
|
|
616
|
+
let (query_result, cause) = match query_result {
|
|
617
|
+
LegacyQueryResult::Succeeded(s) => (s, WorkflowTaskFailedCause::Unspecified),
|
|
618
|
+
#[allow(deprecated)]
|
|
619
|
+
LegacyQueryResult::Failed(f) => {
|
|
620
|
+
let cause = f.force_cause();
|
|
621
|
+
failure = f.failure.clone();
|
|
622
|
+
(legacy_query_failure(f), cause)
|
|
623
|
+
}
|
|
624
|
+
};
|
|
583
625
|
let (_, completed_type, query_result, error_message) = query_result.into_components();
|
|
626
|
+
|
|
584
627
|
Ok(self
|
|
585
628
|
.cloned_client()
|
|
586
629
|
.respond_query_task_completed(RespondQueryTaskCompletedRequest {
|
|
@@ -589,8 +632,8 @@ impl WorkerClient for WorkerClientBag {
|
|
|
589
632
|
query_result,
|
|
590
633
|
error_message,
|
|
591
634
|
namespace: self.namespace.clone(),
|
|
592
|
-
|
|
593
|
-
|
|
635
|
+
failure,
|
|
636
|
+
cause: cause.into(),
|
|
594
637
|
})
|
|
595
638
|
.await?
|
|
596
639
|
.into_inner())
|
|
@@ -612,6 +655,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
612
655
|
identity: self.identity.clone(),
|
|
613
656
|
sticky_task_queue,
|
|
614
657
|
reason: "graceful shutdown".to_string(),
|
|
658
|
+
worker_heartbeat: self.capture_heartbeat(),
|
|
615
659
|
};
|
|
616
660
|
|
|
617
661
|
Ok(
|
|
@@ -626,6 +670,21 @@ impl WorkerClient for WorkerClientBag {
|
|
|
626
670
|
*replaceable_client = new_client;
|
|
627
671
|
}
|
|
628
672
|
|
|
673
|
+
async fn record_worker_heartbeat(
|
|
674
|
+
&self,
|
|
675
|
+
heartbeat: WorkerHeartbeat,
|
|
676
|
+
) -> Result<RecordWorkerHeartbeatResponse> {
|
|
677
|
+
Ok(self
|
|
678
|
+
.cloned_client()
|
|
679
|
+
.record_worker_heartbeat(RecordWorkerHeartbeatRequest {
|
|
680
|
+
namespace: self.namespace.clone(),
|
|
681
|
+
identity: self.identity.clone(),
|
|
682
|
+
worker_heartbeat: vec![heartbeat],
|
|
683
|
+
})
|
|
684
|
+
.await?
|
|
685
|
+
.into_inner())
|
|
686
|
+
}
|
|
687
|
+
|
|
629
688
|
fn capabilities(&self) -> Option<Capabilities> {
|
|
630
689
|
let client = self.replaceable_client.read();
|
|
631
690
|
client.get_client().inner().capabilities().cloned()
|
|
@@ -645,6 +704,10 @@ impl WorkerClient for WorkerClientBag {
|
|
|
645
704
|
let opts = lock.get_client().inner().options();
|
|
646
705
|
(opts.client_name.clone(), opts.client_version.clone())
|
|
647
706
|
}
|
|
707
|
+
|
|
708
|
+
fn get_identity(&self) -> String {
|
|
709
|
+
self.identity.clone()
|
|
710
|
+
}
|
|
648
711
|
}
|
|
649
712
|
|
|
650
713
|
impl NamespacedClient for WorkerClientBag {
|