@temporalio/core-bridge 1.11.2 → 1.11.4
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 +396 -489
- package/Cargo.toml +3 -2
- package/lib/errors.d.ts +2 -0
- package/lib/errors.js +7 -3
- package/lib/errors.js.map +1 -1
- package/lib/index.d.ts +8 -2
- package/lib/index.js.map +1 -1
- package/lib/worker-tuner.d.ts +111 -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/.github/workflows/per-pr.yml +3 -3
- package/sdk-core/Cargo.toml +0 -1
- package/sdk-core/client/Cargo.toml +1 -2
- package/sdk-core/client/src/lib.rs +21 -13
- package/sdk-core/client/src/metrics.rs +1 -1
- package/sdk-core/client/src/raw.rs +46 -1
- package/sdk-core/core/Cargo.toml +7 -7
- package/sdk-core/core/benches/workflow_replay.rs +1 -1
- package/sdk-core/core/src/abstractions/take_cell.rs +1 -1
- package/sdk-core/core/src/abstractions.rs +98 -10
- package/sdk-core/core/src/core_tests/activity_tasks.rs +8 -2
- package/sdk-core/core/src/core_tests/local_activities.rs +1 -1
- package/sdk-core/core/src/core_tests/mod.rs +3 -3
- package/sdk-core/core/src/core_tests/updates.rs +104 -9
- package/sdk-core/core/src/core_tests/workers.rs +72 -3
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +6 -7
- package/sdk-core/core/src/debug_client.rs +78 -0
- package/sdk-core/core/src/ephemeral_server/mod.rs +15 -5
- package/sdk-core/core/src/lib.rs +30 -4
- package/sdk-core/core/src/pollers/mod.rs +1 -1
- package/sdk-core/core/src/pollers/poll_buffer.rs +7 -7
- package/sdk-core/core/src/replay/mod.rs +4 -4
- package/sdk-core/core/src/telemetry/log_export.rs +2 -2
- package/sdk-core/core/src/telemetry/metrics.rs +69 -1
- package/sdk-core/core/src/telemetry/otel.rs +2 -2
- package/sdk-core/core/src/test_help/mod.rs +3 -3
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +3 -3
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +1 -1
- package/sdk-core/core/src/worker/activities/local_activities.rs +68 -24
- package/sdk-core/core/src/worker/activities.rs +26 -15
- package/sdk-core/core/src/worker/client/mocks.rs +10 -4
- package/sdk-core/core/src/worker/client.rs +17 -0
- package/sdk-core/core/src/worker/mod.rs +71 -13
- package/sdk-core/core/src/worker/slot_provider.rs +5 -7
- package/sdk-core/core/src/worker/tuner/fixed_size.rs +4 -3
- package/sdk-core/core/src/worker/tuner/resource_based.rs +171 -32
- package/sdk-core/core/src/worker/tuner.rs +18 -6
- package/sdk-core/core/src/worker/workflow/history_update.rs +43 -13
- package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +6 -6
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +6 -5
- package/sdk-core/core/src/worker/workflow/managed_run.rs +3 -3
- package/sdk-core/core/src/worker/workflow/mod.rs +13 -7
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +7 -7
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +2 -2
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +11 -11
- package/sdk-core/core-api/Cargo.toml +1 -0
- package/sdk-core/core-api/src/worker.rs +84 -30
- package/sdk-core/sdk/Cargo.toml +1 -2
- package/sdk-core/sdk/src/lib.rs +1 -1
- package/sdk-core/sdk/src/workflow_context.rs +9 -8
- package/sdk-core/sdk/src/workflow_future.rs +19 -14
- package/sdk-core/sdk-core-protos/Cargo.toml +2 -0
- package/sdk-core/sdk-core-protos/build.rs +6 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +1 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +3207 -158
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +2934 -118
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/activity/v1/message.proto +67 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +47 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -7
- 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 +5 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +3 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +14 -13
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +22 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +13 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +26 -6
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +5 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +6 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +46 -12
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/update/v1/message.proto +18 -19
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +27 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +192 -19
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +279 -12
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/common/common.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/core_interface.proto +17 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +1 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +30 -6
- package/sdk-core/test-utils/Cargo.toml +1 -2
- package/sdk-core/test-utils/src/lib.rs +2 -2
- package/sdk-core/tests/heavy_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +2 -2
- package/sdk-core/tests/integ_tests/metrics_tests.rs +144 -7
- package/sdk-core/tests/integ_tests/queries_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/update_tests.rs +109 -5
- package/sdk-core/tests/integ_tests/worker_tests.rs +44 -8
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests.rs +3 -2
- package/src/conversions/slot_supplier_bridge.rs +287 -0
- package/src/conversions.rs +23 -15
- package/src/helpers.rs +35 -1
- package/src/runtime.rs +7 -3
- package/src/worker.rs +1 -1
- package/ts/errors.ts +9 -2
- package/ts/index.ts +19 -4
- package/ts/worker-tuner.ts +123 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/.gitmodules +0 -3
|
@@ -25,7 +25,7 @@ use crate::{
|
|
|
25
25
|
};
|
|
26
26
|
use activity_heartbeat_manager::ActivityHeartbeatManager;
|
|
27
27
|
use dashmap::DashMap;
|
|
28
|
-
use
|
|
28
|
+
use futures_util::{
|
|
29
29
|
stream,
|
|
30
30
|
stream::{BoxStream, PollNext},
|
|
31
31
|
Stream, StreamExt,
|
|
@@ -37,14 +37,14 @@ use std::{
|
|
|
37
37
|
atomic::{AtomicBool, Ordering},
|
|
38
38
|
Arc,
|
|
39
39
|
},
|
|
40
|
-
time::{Duration, Instant},
|
|
40
|
+
time::{Duration, Instant, SystemTime},
|
|
41
41
|
};
|
|
42
|
-
use temporal_sdk_core_api::worker::
|
|
42
|
+
use temporal_sdk_core_api::worker::ActivitySlotKind;
|
|
43
43
|
use temporal_sdk_core_protos::{
|
|
44
44
|
coresdk::{
|
|
45
45
|
activity_result::{self as ar, activity_execution_result as aer},
|
|
46
46
|
activity_task::{ActivityCancelReason, ActivityTask},
|
|
47
|
-
ActivityHeartbeat,
|
|
47
|
+
ActivityHeartbeat, ActivitySlotInfo,
|
|
48
48
|
},
|
|
49
49
|
temporal::api::{
|
|
50
50
|
failure::v1::{failure::FailureInfo, ApplicationFailureInfo, CanceledFailureInfo, Failure},
|
|
@@ -93,6 +93,7 @@ struct InFlightActInfo {
|
|
|
93
93
|
/// Only kept for logging reasons
|
|
94
94
|
workflow_run_id: String,
|
|
95
95
|
start_time: Instant,
|
|
96
|
+
scheduled_time: Option<SystemTime>,
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
/// Augments [InFlightActInfo] with details specific to remote activities
|
|
@@ -127,6 +128,7 @@ impl RemoteInFlightActInfo {
|
|
|
127
128
|
workflow_id: wec.workflow_id,
|
|
128
129
|
workflow_run_id: wec.run_id,
|
|
129
130
|
start_time: Instant::now(),
|
|
131
|
+
scheduled_time: poll_resp.scheduled_time.and_then(|i| i.try_into().ok()),
|
|
130
132
|
},
|
|
131
133
|
heartbeat_timeout: poll_resp.heartbeat_timeout,
|
|
132
134
|
issued_cancel_to_lang: None,
|
|
@@ -180,7 +182,7 @@ enum ActivityTaskSource {
|
|
|
180
182
|
impl WorkerActivityTasks {
|
|
181
183
|
#[allow(clippy::too_many_arguments)]
|
|
182
184
|
pub(crate) fn new(
|
|
183
|
-
semaphore:
|
|
185
|
+
semaphore: MeteredPermitDealer<ActivitySlotKind>,
|
|
184
186
|
poller: BoxedActPoller,
|
|
185
187
|
client: Arc<dyn WorkerClient>,
|
|
186
188
|
metrics: MetricsContext,
|
|
@@ -194,7 +196,7 @@ impl WorkerActivityTasks {
|
|
|
194
196
|
let server_poller_stream =
|
|
195
197
|
new_activity_task_poller(poller, metrics.clone(), shutdown_initiated_token.clone());
|
|
196
198
|
let (eager_activities_tx, eager_activities_rx) = unbounded_channel();
|
|
197
|
-
let eager_activities_semaphore = ClosableMeteredPermitDealer::new_arc(semaphore);
|
|
199
|
+
let eager_activities_semaphore = ClosableMeteredPermitDealer::new_arc(Arc::new(semaphore));
|
|
198
200
|
|
|
199
201
|
let start_tasks_stream_complete = CancellationToken::new();
|
|
200
202
|
let starts_stream = Self::merge_start_task_sources(
|
|
@@ -277,7 +279,7 @@ impl WorkerActivityTasks {
|
|
|
277
279
|
// Prefer eager activities over polling the server
|
|
278
280
|
stream::select_with_strategy(non_poll_stream, poller_stream, |_: &mut ()| PollNext::Left)
|
|
279
281
|
.map(|res| Some(res.map_err(Into::into)))
|
|
280
|
-
.chain(
|
|
282
|
+
.chain(futures_util::stream::once(async move {
|
|
281
283
|
on_complete_token.cancel();
|
|
282
284
|
None
|
|
283
285
|
}))
|
|
@@ -336,10 +338,19 @@ impl WorkerActivityTasks {
|
|
|
336
338
|
let _flushing_guard = self.completers_lock.read().await;
|
|
337
339
|
let maybe_net_err = match status {
|
|
338
340
|
aer::Status::WillCompleteAsync(_) => None,
|
|
339
|
-
aer::Status::Completed(ar::Success { result }) =>
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
341
|
+
aer::Status::Completed(ar::Success { result }) => {
|
|
342
|
+
if let Some(sched_time) = act_info
|
|
343
|
+
.base
|
|
344
|
+
.scheduled_time
|
|
345
|
+
.and_then(|st| st.elapsed().ok())
|
|
346
|
+
{
|
|
347
|
+
act_metrics.act_execution_succeeded(sched_time);
|
|
348
|
+
}
|
|
349
|
+
client
|
|
350
|
+
.complete_activity_task(task_token.clone(), result.map(Into::into))
|
|
351
|
+
.await
|
|
352
|
+
.err()
|
|
353
|
+
}
|
|
343
354
|
aer::Status::Failed(ar::Failure { failure }) => {
|
|
344
355
|
act_metrics.act_execution_failed();
|
|
345
356
|
client
|
|
@@ -542,7 +553,7 @@ where
|
|
|
542
553
|
outstanding_entry.insert(RemoteInFlightActInfo::new(
|
|
543
554
|
&task.resp,
|
|
544
555
|
task.permit.into_used(ActivitySlotInfo {
|
|
545
|
-
activity_type: activity_type_name,
|
|
556
|
+
activity_type: activity_type_name.to_string(),
|
|
546
557
|
}),
|
|
547
558
|
));
|
|
548
559
|
// If we have already waited the grace period and issued cancels,
|
|
@@ -739,7 +750,7 @@ mod tests {
|
|
|
739
750
|
.times(2)
|
|
740
751
|
.returning(|_, _| Ok(Default::default()));
|
|
741
752
|
let mock_client = Arc::new(mock_client);
|
|
742
|
-
let sem =
|
|
753
|
+
let sem = fixed_size_permit_dealer(10);
|
|
743
754
|
let shutdown_token = CancellationToken::new();
|
|
744
755
|
let ap = new_activity_task_buffer(
|
|
745
756
|
mock_client.clone(),
|
|
@@ -828,7 +839,7 @@ mod tests {
|
|
|
828
839
|
})
|
|
829
840
|
});
|
|
830
841
|
let mock_client = Arc::new(mock_client);
|
|
831
|
-
let sem =
|
|
842
|
+
let sem = fixed_size_permit_dealer(1);
|
|
832
843
|
let shutdown_token = CancellationToken::new();
|
|
833
844
|
let ap = new_activity_task_buffer(
|
|
834
845
|
mock_client.clone(),
|
|
@@ -899,7 +910,7 @@ mod tests {
|
|
|
899
910
|
.times(2)
|
|
900
911
|
.returning(|_, _| Ok(Default::default()));
|
|
901
912
|
let mock_client = Arc::new(mock_client);
|
|
902
|
-
let sem =
|
|
913
|
+
let sem = fixed_size_permit_dealer(1);
|
|
903
914
|
let shutdown_token = CancellationToken::new();
|
|
904
915
|
let ap = new_activity_task_buffer(
|
|
905
916
|
mock_client.clone(),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
use super::*;
|
|
2
|
-
use
|
|
3
|
-
use once_cell::sync::Lazy;
|
|
2
|
+
use futures_util::Future;
|
|
4
3
|
use std::sync::Arc;
|
|
4
|
+
use std::sync::LazyLock;
|
|
5
5
|
use temporal_client::SlotManager;
|
|
6
6
|
|
|
7
|
-
static DEFAULT_WORKERS_REGISTRY:
|
|
8
|
-
|
|
7
|
+
pub(crate) static DEFAULT_WORKERS_REGISTRY: LazyLock<Arc<SlotManager>> =
|
|
8
|
+
LazyLock::new(|| Arc::new(SlotManager::new()));
|
|
9
9
|
|
|
10
10
|
pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
|
|
11
11
|
signal_and_query_header: true,
|
|
@@ -18,6 +18,7 @@ pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
|
|
|
18
18
|
eager_workflow_start: true,
|
|
19
19
|
sdk_metadata: true,
|
|
20
20
|
count_group_by_execution_status: false,
|
|
21
|
+
nexus: false,
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
#[cfg(test)]
|
|
@@ -29,6 +30,8 @@ pub(crate) fn mock_workflow_client() -> MockWorkerClient {
|
|
|
29
30
|
r.expect_workers()
|
|
30
31
|
.returning(|| DEFAULT_WORKERS_REGISTRY.clone());
|
|
31
32
|
r.expect_is_mock().returning(|| true);
|
|
33
|
+
r.expect_shutdown_worker()
|
|
34
|
+
.returning(|_| Ok(ShutdownWorkerResponse {}));
|
|
32
35
|
r
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -119,6 +122,9 @@ mockall::mock! {
|
|
|
119
122
|
impl Future<Output = Result<DescribeNamespaceResponse>> + Send + 'b
|
|
120
123
|
where 'a: 'b, Self: 'b;
|
|
121
124
|
|
|
125
|
+
fn shutdown_worker<'a, 'b>(&self, sticky_task_queue: String) -> impl Future<Output = Result<ShutdownWorkerResponse>> + Send + 'b
|
|
126
|
+
where 'a: 'b, Self: 'b;
|
|
127
|
+
|
|
122
128
|
fn replace_client(&self, new_client: RetryClient<Client>);
|
|
123
129
|
fn capabilities(&self) -> Option<Capabilities>;
|
|
124
130
|
fn workers(&self) -> Arc<SlotManager>;
|
|
@@ -146,6 +146,7 @@ pub(crate) trait WorkerClient: Sync + Send {
|
|
|
146
146
|
query_result: QueryResult,
|
|
147
147
|
) -> Result<RespondQueryTaskCompletedResponse>;
|
|
148
148
|
async fn describe_namespace(&self) -> Result<DescribeNamespaceResponse>;
|
|
149
|
+
async fn shutdown_worker(&self, sticky_task_queue: String) -> Result<ShutdownWorkerResponse>;
|
|
149
150
|
|
|
150
151
|
fn replace_client(&self, new_client: RetryClient<Client>);
|
|
151
152
|
fn capabilities(&self) -> Option<Capabilities>;
|
|
@@ -232,6 +233,7 @@ impl WorkerClient for WorkerClientBag {
|
|
|
232
233
|
namespace: self.namespace.clone(),
|
|
233
234
|
sdk_metadata: Some(request.sdk_metadata),
|
|
234
235
|
metering_metadata: Some(request.metering_metadata),
|
|
236
|
+
capabilities: None,
|
|
235
237
|
};
|
|
236
238
|
Ok(self
|
|
237
239
|
.cloned_client()
|
|
@@ -384,6 +386,21 @@ impl WorkerClient for WorkerClientBag {
|
|
|
384
386
|
.await
|
|
385
387
|
}
|
|
386
388
|
|
|
389
|
+
async fn shutdown_worker(&self, sticky_task_queue: String) -> Result<ShutdownWorkerResponse> {
|
|
390
|
+
let request = ShutdownWorkerRequest {
|
|
391
|
+
namespace: self.namespace.clone(),
|
|
392
|
+
identity: self.identity.clone(),
|
|
393
|
+
sticky_task_queue,
|
|
394
|
+
reason: "graceful shutdown".to_string(),
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
Ok(
|
|
398
|
+
WorkflowService::shutdown_worker(&mut self.cloned_client(), request)
|
|
399
|
+
.await?
|
|
400
|
+
.into_inner(),
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
|
|
387
404
|
fn replace_client(&self, new_client: RetryClient<Client>) {
|
|
388
405
|
let mut replaceable_client = self.replaceable_client.write();
|
|
389
406
|
*replaceable_client = new_client;
|
|
@@ -26,8 +26,8 @@ use crate::{
|
|
|
26
26
|
protosext::validate_activity_completion,
|
|
27
27
|
telemetry::{
|
|
28
28
|
metrics::{
|
|
29
|
-
activity_poller, activity_worker_type,
|
|
30
|
-
|
|
29
|
+
activity_poller, activity_worker_type, workflow_poller, workflow_sticky_poller,
|
|
30
|
+
workflow_worker_type, MetricsContext,
|
|
31
31
|
},
|
|
32
32
|
TelemetryInstance,
|
|
33
33
|
},
|
|
@@ -49,6 +49,7 @@ use std::{
|
|
|
49
49
|
atomic::{AtomicBool, Ordering},
|
|
50
50
|
Arc,
|
|
51
51
|
},
|
|
52
|
+
time::Duration,
|
|
52
53
|
};
|
|
53
54
|
use temporal_client::{ConfiguredClient, TemporalServiceClientWithMetrics, WorkerKey};
|
|
54
55
|
use temporal_sdk_core_protos::{
|
|
@@ -66,10 +67,13 @@ use temporal_sdk_core_protos::{
|
|
|
66
67
|
},
|
|
67
68
|
TaskToken,
|
|
68
69
|
};
|
|
69
|
-
use tokio::sync::mpsc::unbounded_channel;
|
|
70
|
+
use tokio::sync::{mpsc::unbounded_channel, watch};
|
|
70
71
|
use tokio_stream::wrappers::UnboundedReceiverStream;
|
|
71
72
|
use tokio_util::sync::CancellationToken;
|
|
72
73
|
|
|
74
|
+
use crate::{
|
|
75
|
+
abstractions::PermitDealerContextData, telemetry::metrics::local_activity_worker_type,
|
|
76
|
+
};
|
|
73
77
|
use temporal_sdk_core_api::errors::WorkerValidationError;
|
|
74
78
|
#[cfg(test)]
|
|
75
79
|
use {
|
|
@@ -77,7 +81,7 @@ use {
|
|
|
77
81
|
pollers::{BoxedPoller, MockPermittedPollBuffer},
|
|
78
82
|
protosext::ValidPollWFTQResponse,
|
|
79
83
|
},
|
|
80
|
-
|
|
84
|
+
futures_util::stream::BoxStream,
|
|
81
85
|
temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse,
|
|
82
86
|
};
|
|
83
87
|
|
|
@@ -102,6 +106,22 @@ pub struct Worker {
|
|
|
102
106
|
non_local_activities_complete: Arc<AtomicBool>,
|
|
103
107
|
/// Set when local activities are complete and should stop being polled
|
|
104
108
|
local_activities_complete: Arc<AtomicBool>,
|
|
109
|
+
/// Used to track all permits have been released
|
|
110
|
+
all_permits_tracker: tokio::sync::Mutex<AllPermitsTracker>,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
struct AllPermitsTracker {
|
|
114
|
+
wft_permits: watch::Receiver<usize>,
|
|
115
|
+
act_permits: watch::Receiver<usize>,
|
|
116
|
+
la_permits: watch::Receiver<usize>,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
impl AllPermitsTracker {
|
|
120
|
+
async fn all_done(&mut self) {
|
|
121
|
+
let _ = self.wft_permits.wait_for(|x| *x == 0).await;
|
|
122
|
+
let _ = self.act_permits.wait_for(|x| *x == 0).await;
|
|
123
|
+
let _ = self.la_permits.wait_for(|x| *x == 0).await;
|
|
124
|
+
}
|
|
105
125
|
}
|
|
106
126
|
|
|
107
127
|
#[async_trait::async_trait]
|
|
@@ -270,7 +290,12 @@ impl Worker {
|
|
|
270
290
|
tuner.attach_metrics(meter.clone());
|
|
271
291
|
}
|
|
272
292
|
let shutdown_token = CancellationToken::new();
|
|
273
|
-
let
|
|
293
|
+
let slot_context_data = Arc::new(PermitDealerContextData {
|
|
294
|
+
task_queue: config.task_queue.clone(),
|
|
295
|
+
worker_identity: config.client_identity_override.clone().unwrap_or_default(),
|
|
296
|
+
worker_build_id: config.worker_build_id.clone(),
|
|
297
|
+
});
|
|
298
|
+
let wft_slots = MeteredPermitDealer::new(
|
|
274
299
|
tuner.workflow_task_slot_supplier(),
|
|
275
300
|
metrics.with_new_attrs([workflow_worker_type()]),
|
|
276
301
|
if config.max_cached_workflows > 0 {
|
|
@@ -280,12 +305,16 @@ impl Worker {
|
|
|
280
305
|
} else {
|
|
281
306
|
None
|
|
282
307
|
},
|
|
283
|
-
|
|
284
|
-
|
|
308
|
+
slot_context_data.clone(),
|
|
309
|
+
);
|
|
310
|
+
let wft_permits = wft_slots.get_extant_count_rcv();
|
|
311
|
+
let act_slots = MeteredPermitDealer::new(
|
|
285
312
|
tuner.activity_task_slot_supplier(),
|
|
286
313
|
metrics.with_new_attrs([activity_worker_type()]),
|
|
287
314
|
None,
|
|
288
|
-
|
|
315
|
+
slot_context_data.clone(),
|
|
316
|
+
);
|
|
317
|
+
let act_permits = act_slots.get_extant_count_rcv();
|
|
289
318
|
let (external_wft_tx, external_wft_rx) = unbounded_channel();
|
|
290
319
|
let (wft_stream, act_poller) = match task_pollers {
|
|
291
320
|
TaskPollers::Real => {
|
|
@@ -320,7 +349,7 @@ impl Worker {
|
|
|
320
349
|
normal_name: config.task_queue.clone(),
|
|
321
350
|
},
|
|
322
351
|
max_sticky_polls,
|
|
323
|
-
wft_slots.clone(),
|
|
352
|
+
wft_slots.clone().into_sticky(),
|
|
324
353
|
shutdown_token.child_token(),
|
|
325
354
|
Some(move |np| {
|
|
326
355
|
sticky_metrics.record_num_pollers(np);
|
|
@@ -366,7 +395,8 @@ impl Worker {
|
|
|
366
395
|
wft_stream,
|
|
367
396
|
act_poller,
|
|
368
397
|
} => {
|
|
369
|
-
let ap = act_poller
|
|
398
|
+
let ap = act_poller
|
|
399
|
+
.map(|ap| MockPermittedPollBuffer::new(Arc::new(act_slots.clone()), ap));
|
|
370
400
|
let wft_semaphore = wft_slots.clone();
|
|
371
401
|
let wfs = wft_stream.then(move |s| {
|
|
372
402
|
let wft_semaphore = wft_semaphore.clone();
|
|
@@ -381,11 +411,18 @@ impl Worker {
|
|
|
381
411
|
};
|
|
382
412
|
|
|
383
413
|
let (hb_tx, hb_rx) = unbounded_channel();
|
|
384
|
-
let
|
|
414
|
+
let la_pemit_dealer = MeteredPermitDealer::new(
|
|
385
415
|
tuner.local_activity_slot_supplier(),
|
|
416
|
+
metrics.with_new_attrs([local_activity_worker_type()]),
|
|
417
|
+
None,
|
|
418
|
+
slot_context_data,
|
|
419
|
+
);
|
|
420
|
+
let la_permits = la_pemit_dealer.get_extant_count_rcv();
|
|
421
|
+
let local_act_mgr = Arc::new(LocalActivityManager::new(
|
|
386
422
|
config.namespace.clone(),
|
|
423
|
+
la_pemit_dealer,
|
|
387
424
|
hb_tx,
|
|
388
|
-
metrics.
|
|
425
|
+
metrics.clone(),
|
|
389
426
|
));
|
|
390
427
|
let at_task_mgr = act_poller.map(|ap| {
|
|
391
428
|
WorkerActivityTasks::new(
|
|
@@ -453,6 +490,11 @@ impl Worker {
|
|
|
453
490
|
// Non-local activities are already complete if configured not to poll for them.
|
|
454
491
|
non_local_activities_complete: Arc::new(AtomicBool::new(!poll_on_non_local_activities)),
|
|
455
492
|
local_activities_complete: Default::default(),
|
|
493
|
+
all_permits_tracker: tokio::sync::Mutex::new(AllPermitsTracker {
|
|
494
|
+
wft_permits,
|
|
495
|
+
act_permits,
|
|
496
|
+
la_permits,
|
|
497
|
+
}),
|
|
456
498
|
}
|
|
457
499
|
}
|
|
458
500
|
|
|
@@ -460,6 +502,15 @@ impl Worker {
|
|
|
460
502
|
/// completed
|
|
461
503
|
async fn shutdown(&self) {
|
|
462
504
|
self.initiate_shutdown();
|
|
505
|
+
if let Some(name) = self.workflows.get_sticky_queue_name() {
|
|
506
|
+
// This is a best effort call and we can still shutdown the worker if it fails
|
|
507
|
+
match self.client.shutdown_worker(name).await {
|
|
508
|
+
Err(err) if err.code() != tonic::Code::Unavailable => {
|
|
509
|
+
warn!("Failed to shutdown sticky queue {:?}", err);
|
|
510
|
+
}
|
|
511
|
+
_ => {}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
463
514
|
// We need to wait for all local activities to finish so no more workflow task heartbeats
|
|
464
515
|
// will be generated
|
|
465
516
|
self.local_act_mgr
|
|
@@ -474,6 +525,13 @@ impl Worker {
|
|
|
474
525
|
if let Some(acts) = self.at_task_mgr.as_ref() {
|
|
475
526
|
acts.shutdown().await;
|
|
476
527
|
}
|
|
528
|
+
// Wait for all permits to be released, but don't totally hang real-world shutdown.
|
|
529
|
+
tokio::select! {
|
|
530
|
+
_ = async { self.all_permits_tracker.lock().await.all_done().await } => {},
|
|
531
|
+
_ = tokio::time::sleep(Duration::from_secs(1)) => {
|
|
532
|
+
dbg_panic!("Waiting for all slot permits to release took too long!");
|
|
533
|
+
}
|
|
534
|
+
};
|
|
477
535
|
}
|
|
478
536
|
|
|
479
537
|
/// Finish shutting down by consuming the background pollers and freeing all resources
|
|
@@ -750,7 +808,7 @@ mod tests {
|
|
|
750
808
|
use crate::{
|
|
751
809
|
advance_fut, test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client,
|
|
752
810
|
};
|
|
753
|
-
use
|
|
811
|
+
use futures_util::FutureExt;
|
|
754
812
|
|
|
755
813
|
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
|
|
756
814
|
|
|
@@ -7,8 +7,6 @@ use crate::{
|
|
|
7
7
|
protosext::ValidPollWFTQResponse,
|
|
8
8
|
worker::workflow::wft_poller::validate_wft,
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
use std::sync::Arc;
|
|
12
10
|
use temporal_client::{Slot as SlotTrait, SlotProvider as SlotProviderTrait};
|
|
13
11
|
use temporal_sdk_core_api::worker::WorkflowSlotKind;
|
|
14
12
|
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollWorkflowTaskQueueResponse;
|
|
@@ -58,7 +56,7 @@ impl SlotTrait for Slot {
|
|
|
58
56
|
pub(super) struct SlotProvider {
|
|
59
57
|
namespace: String,
|
|
60
58
|
task_queue: String,
|
|
61
|
-
wft_semaphore:
|
|
59
|
+
wft_semaphore: MeteredPermitDealer<WorkflowSlotKind>,
|
|
62
60
|
external_wft_tx: WFTStreamSender,
|
|
63
61
|
}
|
|
64
62
|
|
|
@@ -66,7 +64,7 @@ impl SlotProvider {
|
|
|
66
64
|
pub(super) fn new(
|
|
67
65
|
namespace: String,
|
|
68
66
|
task_queue: String,
|
|
69
|
-
wft_semaphore:
|
|
67
|
+
wft_semaphore: MeteredPermitDealer<WorkflowSlotKind>,
|
|
70
68
|
external_wft_tx: WFTStreamSender,
|
|
71
69
|
) -> Self {
|
|
72
70
|
Self {
|
|
@@ -119,7 +117,7 @@ mod tests {
|
|
|
119
117
|
|
|
120
118
|
#[tokio::test]
|
|
121
119
|
async fn slot_propagates_through_channel() {
|
|
122
|
-
let wft_semaphore =
|
|
120
|
+
let wft_semaphore = fixed_size_permit_dealer(2);
|
|
123
121
|
let (external_wft_tx, mut external_wft_rx) = unbounded_channel();
|
|
124
122
|
|
|
125
123
|
let provider = SlotProvider::new(
|
|
@@ -142,7 +140,7 @@ mod tests {
|
|
|
142
140
|
let (external_wft_tx, mut external_wft_rx) = unbounded_channel();
|
|
143
141
|
{
|
|
144
142
|
let external_wft_tx = external_wft_tx;
|
|
145
|
-
let wft_semaphore =
|
|
143
|
+
let wft_semaphore = fixed_size_permit_dealer(2);
|
|
146
144
|
let provider = SlotProvider::new(
|
|
147
145
|
"my_namespace".to_string(),
|
|
148
146
|
"my_queue".to_string(),
|
|
@@ -156,7 +154,7 @@ mod tests {
|
|
|
156
154
|
|
|
157
155
|
#[tokio::test]
|
|
158
156
|
async fn unused_slots_reclaimed() {
|
|
159
|
-
let wft_semaphore =
|
|
157
|
+
let wft_semaphore = fixed_size_permit_dealer(2);
|
|
160
158
|
{
|
|
161
159
|
let wft_semaphore = wft_semaphore.clone();
|
|
162
160
|
let (external_wft_tx, _) = unbounded_channel();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
use std::{marker::PhantomData, sync::Arc};
|
|
2
2
|
use temporal_sdk_core_api::worker::{
|
|
3
|
-
SlotKind, SlotReservationContext, SlotSupplier,
|
|
3
|
+
SlotKind, SlotMarkUsedContext, SlotReleaseContext, SlotReservationContext, SlotSupplier,
|
|
4
|
+
SlotSupplierPermit,
|
|
4
5
|
};
|
|
5
6
|
use tokio::sync::Semaphore;
|
|
6
7
|
|
|
@@ -42,9 +43,9 @@ where
|
|
|
42
43
|
perm.ok().map(SlotSupplierPermit::with_user_data)
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
fn mark_slot_used(&self,
|
|
46
|
+
fn mark_slot_used(&self, _ctx: &dyn SlotMarkUsedContext<SlotKind = Self::SlotKind>) {}
|
|
46
47
|
|
|
47
|
-
fn release_slot(&self) {}
|
|
48
|
+
fn release_slot(&self, _ctx: &dyn SlotReleaseContext<SlotKind = Self::SlotKind>) {}
|
|
48
49
|
|
|
49
50
|
fn available_slots(&self) -> Option<usize> {
|
|
50
51
|
Some(self.sem.available_permits())
|