@temporalio/core-bridge 1.12.1 → 1.12.3
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/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/callback_based.rs +123 -0
- package/sdk-core/client/src/lib.rs +113 -36
- package/sdk-core/client/src/metrics.rs +89 -27
- package/sdk-core/client/src/raw.rs +73 -16
- package/sdk-core/client/src/retry.rs +12 -3
- 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 +12 -1
- package/sdk-core/core/src/pollers/poll_buffer.rs +64 -16
- 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 +72 -5
- package/sdk-core/core/src/worker/heartbeat.rs +231 -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 +51 -0
- package/sdk-core/core-c-bridge/build.rs +26 -0
- package/sdk-core/core-c-bridge/include/temporal-sdk-core-c-bridge.h +922 -0
- package/sdk-core/core-c-bridge/src/client.rs +936 -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 +655 -0
- package/sdk-core/core-c-bridge/src/tests/mod.rs +354 -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_cloud_upstream/CODEOWNERS +1 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/README.md +1 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.yaml +1 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +83 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +37 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/connectivityrule/v1/message.proto +64 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +3 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +10 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +1 -0
- 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 +1285 -103
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +1290 -122
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +64 -6
- 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 +86 -17
- 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/batch_operation.proto +3 -0
- 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/enums/v1/task_queue.proto +11 -0
- 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 +52 -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/sdk/v1/task_complete_metadata.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/worker_config.proto +36 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +29 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/worker/v1/message.proto +144 -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 +268 -39
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +62 -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 +100 -6
- package/sdk-core/test-utils/Cargo.toml +1 -0
- package/sdk-core/test-utils/src/lib.rs +101 -6
- package/sdk-core/tests/cloud_tests.rs +11 -74
- package/sdk-core/tests/heavy_tests.rs +11 -3
- package/sdk-core/tests/integ_tests/client_tests.rs +22 -19
- 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 +8 -3
- package/sdk-core/tests/integ_tests/worker_versioning_tests.rs +3 -7
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +13 -0
- 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/sdk-core/tests/integ_tests/workflow_tests/priority.rs +2 -108
- package/sdk-core/tests/main.rs +3 -0
- package/sdk-core/tests/shared_tests/mod.rs +43 -0
- package/sdk-core/tests/shared_tests/priority.rs +155 -0
- package/src/client.rs +11 -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: Option<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: Option<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,19 @@ impl WorkerClientBag {
|
|
|
114
127
|
None
|
|
115
128
|
}
|
|
116
129
|
}
|
|
130
|
+
|
|
131
|
+
fn capture_heartbeat(&self) -> Option<WorkerHeartbeat> {
|
|
132
|
+
if let Some(heartbeat_data) = self.heartbeat_data.as_ref() {
|
|
133
|
+
if let Some(hb) = heartbeat_data.get() {
|
|
134
|
+
hb()
|
|
135
|
+
} else {
|
|
136
|
+
dbg_panic!("Heartbeat function never set");
|
|
137
|
+
None
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
None
|
|
141
|
+
}
|
|
142
|
+
}
|
|
117
143
|
}
|
|
118
144
|
|
|
119
145
|
/// This trait contains everything workers need to interact with Temporal, and hence provides a
|
|
@@ -197,12 +223,17 @@ pub trait WorkerClient: Sync + Send {
|
|
|
197
223
|
async fn respond_legacy_query(
|
|
198
224
|
&self,
|
|
199
225
|
task_token: TaskToken,
|
|
200
|
-
query_result:
|
|
226
|
+
query_result: LegacyQueryResult,
|
|
201
227
|
) -> Result<RespondQueryTaskCompletedResponse>;
|
|
202
228
|
/// Describe the namespace
|
|
203
229
|
async fn describe_namespace(&self) -> Result<DescribeNamespaceResponse>;
|
|
204
230
|
/// Shutdown the worker
|
|
205
231
|
async fn shutdown_worker(&self, sticky_task_queue: String) -> Result<ShutdownWorkerResponse>;
|
|
232
|
+
/// Record a worker heartbeat
|
|
233
|
+
async fn record_worker_heartbeat(
|
|
234
|
+
&self,
|
|
235
|
+
heartbeat: WorkerHeartbeat,
|
|
236
|
+
) -> Result<RecordWorkerHeartbeatResponse>;
|
|
206
237
|
|
|
207
238
|
/// Replace the underlying client
|
|
208
239
|
fn replace_client(&self, new_client: RetryClient<Client>);
|
|
@@ -214,6 +245,8 @@ pub trait WorkerClient: Sync + Send {
|
|
|
214
245
|
fn is_mock(&self) -> bool;
|
|
215
246
|
/// Return name and version of the SDK
|
|
216
247
|
fn sdk_name_and_version(&self) -> (String, String);
|
|
248
|
+
/// Get worker identity
|
|
249
|
+
fn get_identity(&self) -> String;
|
|
217
250
|
}
|
|
218
251
|
|
|
219
252
|
/// Configuration options shared by workflow, activity, and Nexus polling calls
|
|
@@ -267,6 +300,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
267
300
|
binary_checksum: self.binary_checksum(),
|
|
268
301
|
worker_version_capabilities: self.worker_version_capabilities(),
|
|
269
302
|
deployment_options: self.deployment_options(),
|
|
303
|
+
worker_heartbeat: None,
|
|
270
304
|
}
|
|
271
305
|
.into_request();
|
|
272
306
|
request.extensions_mut().insert(IsWorkerTaskLongPoll);
|
|
@@ -303,6 +337,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
303
337
|
}),
|
|
304
338
|
worker_version_capabilities: self.worker_version_capabilities(),
|
|
305
339
|
deployment_options: self.deployment_options(),
|
|
340
|
+
worker_heartbeat: None,
|
|
306
341
|
}
|
|
307
342
|
.into_request();
|
|
308
343
|
request.extensions_mut().insert(IsWorkerTaskLongPoll);
|
|
@@ -335,6 +370,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
335
370
|
identity: self.identity.clone(),
|
|
336
371
|
worker_version_capabilities: self.worker_version_capabilities(),
|
|
337
372
|
deployment_options: self.deployment_options(),
|
|
373
|
+
worker_heartbeat: self.capture_heartbeat().into_iter().collect(),
|
|
338
374
|
}
|
|
339
375
|
.into_request();
|
|
340
376
|
request.extensions_mut().insert(IsWorkerTaskLongPoll);
|
|
@@ -578,9 +614,20 @@ impl WorkerClient for WorkerClientBag {
|
|
|
578
614
|
async fn respond_legacy_query(
|
|
579
615
|
&self,
|
|
580
616
|
task_token: TaskToken,
|
|
581
|
-
query_result:
|
|
617
|
+
query_result: LegacyQueryResult,
|
|
582
618
|
) -> Result<RespondQueryTaskCompletedResponse> {
|
|
619
|
+
let mut failure = None;
|
|
620
|
+
let (query_result, cause) = match query_result {
|
|
621
|
+
LegacyQueryResult::Succeeded(s) => (s, WorkflowTaskFailedCause::Unspecified),
|
|
622
|
+
#[allow(deprecated)]
|
|
623
|
+
LegacyQueryResult::Failed(f) => {
|
|
624
|
+
let cause = f.force_cause();
|
|
625
|
+
failure = f.failure.clone();
|
|
626
|
+
(legacy_query_failure(f), cause)
|
|
627
|
+
}
|
|
628
|
+
};
|
|
583
629
|
let (_, completed_type, query_result, error_message) = query_result.into_components();
|
|
630
|
+
|
|
584
631
|
Ok(self
|
|
585
632
|
.cloned_client()
|
|
586
633
|
.respond_query_task_completed(RespondQueryTaskCompletedRequest {
|
|
@@ -589,8 +636,8 @@ impl WorkerClient for WorkerClientBag {
|
|
|
589
636
|
query_result,
|
|
590
637
|
error_message,
|
|
591
638
|
namespace: self.namespace.clone(),
|
|
592
|
-
|
|
593
|
-
|
|
639
|
+
failure,
|
|
640
|
+
cause: cause.into(),
|
|
594
641
|
})
|
|
595
642
|
.await?
|
|
596
643
|
.into_inner())
|
|
@@ -612,6 +659,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
612
659
|
identity: self.identity.clone(),
|
|
613
660
|
sticky_task_queue,
|
|
614
661
|
reason: "graceful shutdown".to_string(),
|
|
662
|
+
worker_heartbeat: self.capture_heartbeat(),
|
|
615
663
|
};
|
|
616
664
|
|
|
617
665
|
Ok(
|
|
@@ -626,6 +674,21 @@ impl WorkerClient for WorkerClientBag {
|
|
|
626
674
|
*replaceable_client = new_client;
|
|
627
675
|
}
|
|
628
676
|
|
|
677
|
+
async fn record_worker_heartbeat(
|
|
678
|
+
&self,
|
|
679
|
+
heartbeat: WorkerHeartbeat,
|
|
680
|
+
) -> Result<RecordWorkerHeartbeatResponse> {
|
|
681
|
+
Ok(self
|
|
682
|
+
.cloned_client()
|
|
683
|
+
.record_worker_heartbeat(RecordWorkerHeartbeatRequest {
|
|
684
|
+
namespace: self.namespace.clone(),
|
|
685
|
+
identity: self.identity.clone(),
|
|
686
|
+
worker_heartbeat: vec![heartbeat],
|
|
687
|
+
})
|
|
688
|
+
.await?
|
|
689
|
+
.into_inner())
|
|
690
|
+
}
|
|
691
|
+
|
|
629
692
|
fn capabilities(&self) -> Option<Capabilities> {
|
|
630
693
|
let client = self.replaceable_client.read();
|
|
631
694
|
client.get_client().inner().capabilities().cloned()
|
|
@@ -645,6 +708,10 @@ impl WorkerClient for WorkerClientBag {
|
|
|
645
708
|
let opts = lock.get_client().inner().options();
|
|
646
709
|
(opts.client_name.clone(), opts.client_version.clone())
|
|
647
710
|
}
|
|
711
|
+
|
|
712
|
+
fn get_identity(&self) -> String {
|
|
713
|
+
self.identity.clone()
|
|
714
|
+
}
|
|
648
715
|
}
|
|
649
716
|
|
|
650
717
|
impl NamespacedClient for WorkerClientBag {
|