@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
syntax = "proto3";
|
|
2
2
|
|
|
3
3
|
package coresdk;
|
|
4
|
-
option ruby_package = "Temporalio::Bridge::Api::CoreInterface";
|
|
4
|
+
option ruby_package = "Temporalio::Internal::Bridge::Api::CoreInterface";
|
|
5
5
|
|
|
6
6
|
// Note: Intellij will think the Google imports don't work because of the slightly odd nature of
|
|
7
7
|
// the include paths. You can make it work by going to the "Protobuf Support" settings section
|
|
@@ -29,3 +29,19 @@ message ActivityTaskCompletion {
|
|
|
29
29
|
bytes task_token = 1;
|
|
30
30
|
activity_result.ActivityExecutionResult result = 2;
|
|
31
31
|
}
|
|
32
|
+
|
|
33
|
+
// Info about workflow task slot usage
|
|
34
|
+
message WorkflowSlotInfo {
|
|
35
|
+
string workflow_type = 1;
|
|
36
|
+
bool is_sticky = 2;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Info about activity task slot usage
|
|
40
|
+
message ActivitySlotInfo {
|
|
41
|
+
string activity_type = 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Info about local activity slot usage
|
|
45
|
+
message LocalActivitySlotInfo {
|
|
46
|
+
string activity_type = 1;
|
|
47
|
+
}
|
package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/external_data/external_data.proto
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
syntax = "proto3";
|
|
2
2
|
|
|
3
3
|
package coresdk.external_data;
|
|
4
|
-
option ruby_package = "Temporalio::Bridge::Api::ExternalData";
|
|
4
|
+
option ruby_package = "Temporalio::Internal::Bridge::Api::ExternalData";
|
|
5
5
|
|
|
6
6
|
import "google/protobuf/duration.proto";
|
|
7
7
|
import "google/protobuf/timestamp.proto";
|
|
@@ -5,7 +5,7 @@ syntax = "proto3";
|
|
|
5
5
|
* lang SDK applies these activation jobs to drive workflows.
|
|
6
6
|
*/
|
|
7
7
|
package coresdk.workflow_activation;
|
|
8
|
-
option ruby_package = "Temporalio::Bridge::Api::WorkflowActivation";
|
|
8
|
+
option ruby_package = "Temporalio::Internal::Bridge::Api::WorkflowActivation";
|
|
9
9
|
|
|
10
10
|
import "google/protobuf/timestamp.proto";
|
|
11
11
|
import "google/protobuf/duration.proto";
|
|
@@ -6,7 +6,7 @@ syntax = "proto3";
|
|
|
6
6
|
* activation.
|
|
7
7
|
*/
|
|
8
8
|
package coresdk.workflow_commands;
|
|
9
|
-
option ruby_package = "Temporalio::Bridge::Api::WorkflowCommands";
|
|
9
|
+
option ruby_package = "Temporalio::Internal::Bridge::Api::WorkflowCommands";
|
|
10
10
|
|
|
11
11
|
import "google/protobuf/duration.proto";
|
|
12
12
|
import "google/protobuf/timestamp.proto";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
syntax = "proto3";
|
|
2
2
|
|
|
3
3
|
package coresdk.workflow_completion;
|
|
4
|
-
option ruby_package = "Temporalio::Bridge::Api::WorkflowCompletion";
|
|
4
|
+
option ruby_package = "Temporalio::Internal::Bridge::Api::WorkflowCompletion";
|
|
5
5
|
|
|
6
6
|
import "temporal/api/failure/v1/message.proto";
|
|
7
7
|
import "temporal/api/enums/v1/failed_cause.proto";
|
|
@@ -111,6 +111,7 @@ pub mod coresdk {
|
|
|
111
111
|
temporal::api::enums::v1::TimeoutType,
|
|
112
112
|
};
|
|
113
113
|
use activity_execution_result as aer;
|
|
114
|
+
use anyhow::anyhow;
|
|
114
115
|
use std::fmt::{Display, Formatter};
|
|
115
116
|
|
|
116
117
|
impl ActivityExecutionResult {
|
|
@@ -209,13 +210,23 @@ pub mod coresdk {
|
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
impl ActivityResolution {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
/// Extract an activity's payload if it completed successfully, or return an error for all
|
|
214
|
+
/// other outcomes.
|
|
215
|
+
pub fn success_payload_or_error(self) -> Result<Option<Payload>, anyhow::Error> {
|
|
216
|
+
let Some(status) = self.status else {
|
|
217
|
+
return Err(anyhow!("Activity completed without a status"));
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
match status {
|
|
221
|
+
activity_resolution::Status::Completed(success) => Ok(success.result),
|
|
222
|
+
e => Err(anyhow!("Activity was not successful: {e:?}")),
|
|
216
223
|
}
|
|
217
224
|
}
|
|
218
225
|
|
|
226
|
+
pub fn unwrap_ok_payload(self) -> Payload {
|
|
227
|
+
self.success_payload_or_error().unwrap().unwrap()
|
|
228
|
+
}
|
|
229
|
+
|
|
219
230
|
pub fn completed_ok(&self) -> bool {
|
|
220
231
|
matches!(self.status, Some(activity_resolution::Status::Completed(_)))
|
|
221
232
|
}
|
|
@@ -1447,6 +1458,11 @@ pub mod coresdk {
|
|
|
1447
1458
|
// This is disgusting, but unclear to me how to avoid it. TODO: Discuss w/ prost maintainer
|
|
1448
1459
|
pub mod temporal {
|
|
1449
1460
|
pub mod api {
|
|
1461
|
+
pub mod activity {
|
|
1462
|
+
pub mod v1 {
|
|
1463
|
+
tonic::include_proto!("temporal.api.activity.v1");
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1450
1466
|
pub mod batch {
|
|
1451
1467
|
pub mod v1 {
|
|
1452
1468
|
tonic::include_proto!("temporal.api.batch.v1");
|
|
@@ -1754,9 +1770,11 @@ pub mod temporal {
|
|
|
1754
1770
|
}
|
|
1755
1771
|
}
|
|
1756
1772
|
|
|
1757
|
-
impl
|
|
1773
|
+
impl std::fmt::Debug for Payload {
|
|
1758
1774
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
1759
|
-
if
|
|
1775
|
+
if std::env::var("TEMPORAL_PRINT_FULL_PAYLOADS").is_err()
|
|
1776
|
+
&& self.data.len() > 64
|
|
1777
|
+
{
|
|
1760
1778
|
let mut windows = self.data.as_slice().windows(32);
|
|
1761
1779
|
write!(
|
|
1762
1780
|
f,
|
|
@@ -1770,6 +1788,12 @@ pub mod temporal {
|
|
|
1770
1788
|
}
|
|
1771
1789
|
}
|
|
1772
1790
|
|
|
1791
|
+
impl Display for Payload {
|
|
1792
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
1793
|
+
write!(f, "{:?}", self)
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1773
1797
|
impl Display for Header {
|
|
1774
1798
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
1775
1799
|
write!(f, "Header(")?;
|
|
@@ -18,9 +18,8 @@ anyhow = "1.0"
|
|
|
18
18
|
async-trait = "0.1"
|
|
19
19
|
base64 = "0.22"
|
|
20
20
|
bytes = "1.3"
|
|
21
|
-
futures = "0.3"
|
|
21
|
+
futures-util = { version = "0.3", default-features = false }
|
|
22
22
|
log = "0.4"
|
|
23
|
-
once_cell = { workspace = true }
|
|
24
23
|
parking_lot = "0.12"
|
|
25
24
|
prost = { workspace = true }
|
|
26
25
|
prost-types = { workspace = true }
|
|
@@ -13,7 +13,7 @@ pub use temporal_sdk_core::replay::HistoryForReplay;
|
|
|
13
13
|
use crate::stream::{Stream, TryStreamExt};
|
|
14
14
|
use anyhow::{Context, Error};
|
|
15
15
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
|
16
|
-
use
|
|
16
|
+
use futures_util::{future, stream, stream::FuturesUnordered, StreamExt};
|
|
17
17
|
use parking_lot::Mutex;
|
|
18
18
|
use prost::Message;
|
|
19
19
|
use rand::{distributions::Standard, Rng};
|
|
@@ -154,7 +154,7 @@ pub async fn history_from_proto_binary(path_from_root: &str) -> Result<History,
|
|
|
154
154
|
Ok(History::decode(&*bytes)?)
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
static INTEG_TESTS_RT:
|
|
157
|
+
static INTEG_TESTS_RT: std::sync::OnceLock<CoreRuntime> = std::sync::OnceLock::new();
|
|
158
158
|
pub fn init_integ_telem() -> &'static CoreRuntime {
|
|
159
159
|
INTEG_TESTS_RT.get_or_init(|| {
|
|
160
160
|
let telemetry_options = get_integ_telem_options();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
use
|
|
2
|
-
use
|
|
1
|
+
use futures_util::stream;
|
|
2
|
+
use futures_util::TryStreamExt;
|
|
3
3
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
4
4
|
use temporal_client::{ClientOptionsBuilder, TestService, WorkflowService};
|
|
5
5
|
use temporal_sdk_core::ephemeral_server::{
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
use anyhow::anyhow;
|
|
1
2
|
use assert_matches::assert_matches;
|
|
2
3
|
use std::{env, net::SocketAddr, sync::Arc, time::Duration};
|
|
3
4
|
use temporal_client::{WorkflowClientTrait, WorkflowOptions, WorkflowService};
|
|
5
|
+
use temporal_sdk::{
|
|
6
|
+
ActContext, ActivityError, ActivityOptions, CancellableFuture, LocalActivityOptions, WfContext,
|
|
7
|
+
};
|
|
4
8
|
use temporal_sdk_core::{
|
|
5
9
|
init_worker,
|
|
6
10
|
telemetry::{build_otlp_metric_exporter, start_prometheus_metric_exporter},
|
|
7
|
-
CoreRuntime,
|
|
11
|
+
CoreRuntime, TokioRuntimeBuilder,
|
|
8
12
|
};
|
|
9
13
|
use temporal_sdk_core_api::{
|
|
10
14
|
telemetry::{
|
|
@@ -25,9 +29,10 @@ use temporal_sdk_core_protos::{
|
|
|
25
29
|
ScheduleActivity, ScheduleLocalActivity,
|
|
26
30
|
},
|
|
27
31
|
workflow_completion::WorkflowActivationCompletion,
|
|
28
|
-
ActivityTaskCompletion,
|
|
32
|
+
ActivityTaskCompletion, AsJsonPayloadExt,
|
|
29
33
|
},
|
|
30
34
|
temporal::api::{
|
|
35
|
+
common::v1::RetryPolicy,
|
|
31
36
|
enums::v1::WorkflowIdReusePolicy,
|
|
32
37
|
failure::v1::Failure,
|
|
33
38
|
query::v1::WorkflowQuery,
|
|
@@ -491,11 +496,8 @@ async fn query_of_closed_workflow_doesnt_tick_terminal_metric(
|
|
|
491
496
|
|
|
492
497
|
#[test]
|
|
493
498
|
fn runtime_new() {
|
|
494
|
-
let mut rt =
|
|
495
|
-
get_integ_telem_options(),
|
|
496
|
-
tokio::runtime::Builder::new_multi_thread(),
|
|
497
|
-
)
|
|
498
|
-
.unwrap();
|
|
499
|
+
let mut rt =
|
|
500
|
+
CoreRuntime::new(get_integ_telem_options(), TokioRuntimeBuilder::default()).unwrap();
|
|
499
501
|
let handle = rt.tokio_handle();
|
|
500
502
|
let _rt = handle.enter();
|
|
501
503
|
let (telemopts, addr, _aborter) = prom_metrics(false, false);
|
|
@@ -620,3 +622,138 @@ async fn request_fail_codes_otel() {
|
|
|
620
622
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
621
623
|
}
|
|
622
624
|
}
|
|
625
|
+
|
|
626
|
+
#[tokio::test]
|
|
627
|
+
async fn activity_metrics() {
|
|
628
|
+
let (telemopts, addr, _aborter) = prom_metrics(false, false);
|
|
629
|
+
let rt = CoreRuntime::new_assume_tokio(telemopts).unwrap();
|
|
630
|
+
let wf_name = "activity_metrics";
|
|
631
|
+
let mut starter = CoreWfStarter::new_with_runtime(wf_name, rt);
|
|
632
|
+
let task_queue = starter.get_task_queue().to_owned();
|
|
633
|
+
let mut worker = starter.worker().await;
|
|
634
|
+
|
|
635
|
+
worker.register_wf(wf_name.to_string(), |ctx: WfContext| async move {
|
|
636
|
+
let normal_act_pass = ctx.activity(ActivityOptions {
|
|
637
|
+
activity_type: "pass_fail_act".to_string(),
|
|
638
|
+
input: "pass".as_json_payload().expect("serializes fine"),
|
|
639
|
+
start_to_close_timeout: Some(Duration::from_secs(1)),
|
|
640
|
+
..Default::default()
|
|
641
|
+
});
|
|
642
|
+
let local_act_pass = ctx.local_activity(LocalActivityOptions {
|
|
643
|
+
activity_type: "pass_fail_act".to_string(),
|
|
644
|
+
input: "pass".as_json_payload().expect("serializes fine"),
|
|
645
|
+
..Default::default()
|
|
646
|
+
});
|
|
647
|
+
let normal_act_fail = ctx.activity(ActivityOptions {
|
|
648
|
+
activity_type: "pass_fail_act".to_string(),
|
|
649
|
+
input: "fail".as_json_payload().expect("serializes fine"),
|
|
650
|
+
start_to_close_timeout: Some(Duration::from_secs(1)),
|
|
651
|
+
retry_policy: Some(RetryPolicy {
|
|
652
|
+
maximum_attempts: 1,
|
|
653
|
+
..Default::default()
|
|
654
|
+
}),
|
|
655
|
+
..Default::default()
|
|
656
|
+
});
|
|
657
|
+
let local_act_fail = ctx.local_activity(LocalActivityOptions {
|
|
658
|
+
activity_type: "pass_fail_act".to_string(),
|
|
659
|
+
input: "fail".as_json_payload().expect("serializes fine"),
|
|
660
|
+
retry_policy: RetryPolicy {
|
|
661
|
+
maximum_attempts: 1,
|
|
662
|
+
..Default::default()
|
|
663
|
+
},
|
|
664
|
+
..Default::default()
|
|
665
|
+
});
|
|
666
|
+
let local_act_cancel = ctx.local_activity(LocalActivityOptions {
|
|
667
|
+
activity_type: "pass_fail_act".to_string(),
|
|
668
|
+
input: "cancel".as_json_payload().expect("serializes fine"),
|
|
669
|
+
retry_policy: RetryPolicy {
|
|
670
|
+
maximum_attempts: 1,
|
|
671
|
+
..Default::default()
|
|
672
|
+
},
|
|
673
|
+
..Default::default()
|
|
674
|
+
});
|
|
675
|
+
join!(
|
|
676
|
+
normal_act_pass,
|
|
677
|
+
local_act_pass,
|
|
678
|
+
normal_act_fail,
|
|
679
|
+
local_act_fail
|
|
680
|
+
);
|
|
681
|
+
local_act_cancel.cancel(&ctx);
|
|
682
|
+
local_act_cancel.await;
|
|
683
|
+
Ok(().into())
|
|
684
|
+
});
|
|
685
|
+
worker.register_activity("pass_fail_act", |ctx: ActContext, i: String| async move {
|
|
686
|
+
match i.as_str() {
|
|
687
|
+
"pass" => Ok("pass"),
|
|
688
|
+
"cancel" => {
|
|
689
|
+
// TODO: Cancel is taking until shutdown to come through :|
|
|
690
|
+
ctx.cancelled().await;
|
|
691
|
+
Err(ActivityError::cancelled())
|
|
692
|
+
}
|
|
693
|
+
_ => Err(anyhow!("fail").into()),
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
worker
|
|
698
|
+
.submit_wf(
|
|
699
|
+
wf_name.to_owned(),
|
|
700
|
+
wf_name.to_owned(),
|
|
701
|
+
vec![],
|
|
702
|
+
WorkflowOptions::default(),
|
|
703
|
+
)
|
|
704
|
+
.await
|
|
705
|
+
.unwrap();
|
|
706
|
+
worker.run_until_done().await.unwrap();
|
|
707
|
+
|
|
708
|
+
let body = get_text(format!("http://{addr}/metrics")).await;
|
|
709
|
+
assert!(body.contains(&format!(
|
|
710
|
+
"temporal_activity_execution_failed{{activity_type=\"pass_fail_act\",\
|
|
711
|
+
namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
|
|
712
|
+
task_queue=\"{task_queue}\",workflow_type=\"{wf_name}\"}} 1"
|
|
713
|
+
)));
|
|
714
|
+
assert!(body.contains(&format!(
|
|
715
|
+
"temporal_activity_schedule_to_start_latency_count{{\
|
|
716
|
+
namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
|
|
717
|
+
task_queue=\"{task_queue}\"}} 2"
|
|
718
|
+
)));
|
|
719
|
+
assert!(body.contains(&format!(
|
|
720
|
+
"temporal_activity_execution_latency_count{{activity_type=\"pass_fail_act\",\
|
|
721
|
+
namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
|
|
722
|
+
task_queue=\"{task_queue}\",workflow_type=\"{wf_name}\"}} 2"
|
|
723
|
+
)));
|
|
724
|
+
assert!(body.contains(&format!(
|
|
725
|
+
"temporal_activity_succeed_endtoend_latency_count{{activity_type=\"pass_fail_act\",\
|
|
726
|
+
namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
|
|
727
|
+
task_queue=\"{task_queue}\",workflow_type=\"{wf_name}\"}} 1"
|
|
728
|
+
)));
|
|
729
|
+
|
|
730
|
+
assert!(body.contains(&format!(
|
|
731
|
+
"temporal_local_activity_total{{activity_type=\"pass_fail_act\",namespace=\"{NAMESPACE}\",\
|
|
732
|
+
service_name=\"temporal-core-sdk\",task_queue=\"{task_queue}\",\
|
|
733
|
+
workflow_type=\"{wf_name}\"}} 3"
|
|
734
|
+
)));
|
|
735
|
+
assert!(body.contains(&format!(
|
|
736
|
+
"temporal_local_activity_execution_failed{{activity_type=\"pass_fail_act\",\
|
|
737
|
+
namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
|
|
738
|
+
task_queue=\"{task_queue}\",\
|
|
739
|
+
workflow_type=\"{wf_name}\"}} 1"
|
|
740
|
+
)));
|
|
741
|
+
assert!(body.contains(&format!(
|
|
742
|
+
"temporal_local_activity_execution_cancelled{{activity_type=\"pass_fail_act\",\
|
|
743
|
+
namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
|
|
744
|
+
task_queue=\"{task_queue}\",\
|
|
745
|
+
workflow_type=\"{wf_name}\"}} 1"
|
|
746
|
+
)));
|
|
747
|
+
assert!(body.contains(&format!(
|
|
748
|
+
"temporal_local_activity_execution_latency_count{{activity_type=\"pass_fail_act\",\
|
|
749
|
+
namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
|
|
750
|
+
task_queue=\"{task_queue}\",\
|
|
751
|
+
workflow_type=\"{wf_name}\"}} 3"
|
|
752
|
+
)));
|
|
753
|
+
assert!(body.contains(&format!(
|
|
754
|
+
"temporal_local_activity_succeed_endtoend_latency_count{{activity_type=\"pass_fail_act\",\
|
|
755
|
+
namespace=\"{NAMESPACE}\",service_name=\"temporal-core-sdk\",\
|
|
756
|
+
task_queue=\"{task_queue}\",\
|
|
757
|
+
workflow_type=\"{wf_name}\"}} 1"
|
|
758
|
+
)));
|
|
759
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use assert_matches::assert_matches;
|
|
2
|
-
use futures::{prelude::stream::FuturesUnordered, FutureExt, StreamExt};
|
|
3
2
|
use futures_util::future::join_all;
|
|
3
|
+
use futures_util::{stream::FuturesUnordered, FutureExt, StreamExt};
|
|
4
4
|
use std::time::{Duration, Instant};
|
|
5
5
|
use temporal_client::WorkflowClientTrait;
|
|
6
6
|
use temporal_sdk_core_protos::{
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
use anyhow::anyhow;
|
|
2
2
|
use assert_matches::assert_matches;
|
|
3
3
|
use futures_util::{future, future::join_all, StreamExt};
|
|
4
|
-
use once_cell::sync::Lazy;
|
|
5
4
|
use std::{
|
|
6
5
|
sync::{
|
|
7
6
|
atomic::{AtomicBool, AtomicUsize, Ordering},
|
|
8
|
-
Arc,
|
|
7
|
+
Arc, LazyLock,
|
|
9
8
|
},
|
|
10
9
|
time::Duration,
|
|
11
10
|
};
|
|
@@ -78,7 +77,13 @@ async fn update_workflow(#[values(FailUpdate::Yes, FailUpdate::No)] will_fail: F
|
|
|
78
77
|
.unwrap();
|
|
79
78
|
let with_id = HistoryForReplay::new(history, workflow_id.to_string());
|
|
80
79
|
let replay_worker = init_core_replay_preloaded(workflow_id, [with_id]);
|
|
81
|
-
|
|
80
|
+
// Init workflow comes by itself
|
|
81
|
+
let act = replay_worker.poll_workflow_activation().await.unwrap();
|
|
82
|
+
replay_worker
|
|
83
|
+
.complete_workflow_activation(WorkflowActivationCompletion::empty(act.run_id))
|
|
84
|
+
.await
|
|
85
|
+
.unwrap();
|
|
86
|
+
handle_update(will_fail, CompleteWorkflow::Yes, replay_worker.as_ref(), 0).await;
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
#[tokio::test]
|
|
@@ -147,6 +152,12 @@ async fn reapplied_updates_due_to_reset() {
|
|
|
147
152
|
let with_id = HistoryForReplay::new(history, workflow_id.to_string());
|
|
148
153
|
|
|
149
154
|
let replay_worker = init_core_replay_preloaded(workflow_id, [with_id]);
|
|
155
|
+
// Init workflow comes by itself
|
|
156
|
+
let act = replay_worker.poll_workflow_activation().await.unwrap();
|
|
157
|
+
replay_worker
|
|
158
|
+
.complete_workflow_activation(WorkflowActivationCompletion::empty(act.run_id))
|
|
159
|
+
.await
|
|
160
|
+
.unwrap();
|
|
150
161
|
// We now recapitulate the actions that the worker took on first execution above, pretending
|
|
151
162
|
// that we always followed the post-reset history.
|
|
152
163
|
// First, we handled the post-reset reapplied update and did not complete the workflow.
|
|
@@ -154,9 +165,15 @@ async fn reapplied_updates_due_to_reset() {
|
|
|
154
165
|
FailUpdate::No,
|
|
155
166
|
CompleteWorkflow::No,
|
|
156
167
|
replay_worker.as_ref(),
|
|
157
|
-
|
|
168
|
+
1,
|
|
158
169
|
)
|
|
159
170
|
.await;
|
|
171
|
+
// Then the timer fires
|
|
172
|
+
let act = replay_worker.poll_workflow_activation().await.unwrap();
|
|
173
|
+
replay_worker
|
|
174
|
+
.complete_workflow_activation(WorkflowActivationCompletion::empty(act.run_id))
|
|
175
|
+
.await
|
|
176
|
+
.unwrap();
|
|
160
177
|
// Then the client sent a second update; we handled it and completed the workflow.
|
|
161
178
|
handle_update(
|
|
162
179
|
FailUpdate::No,
|
|
@@ -957,7 +974,7 @@ async fn worker_restarted_in_middle_of_update() {
|
|
|
957
974
|
let mut worker = starter.worker().await;
|
|
958
975
|
let client = starter.get_client().await;
|
|
959
976
|
|
|
960
|
-
static BARR:
|
|
977
|
+
static BARR: LazyLock<Barrier> = LazyLock::new(|| Barrier::new(2));
|
|
961
978
|
static ACT_RAN: AtomicBool = AtomicBool::new(false);
|
|
962
979
|
worker.register_wf(wf_name.to_owned(), |ctx: WfContext| async move {
|
|
963
980
|
ctx.update_handler(
|
|
@@ -1044,3 +1061,90 @@ async fn worker_restarted_in_middle_of_update() {
|
|
|
1044
1061
|
.await
|
|
1045
1062
|
.unwrap();
|
|
1046
1063
|
}
|
|
1064
|
+
|
|
1065
|
+
#[tokio::test]
|
|
1066
|
+
async fn update_after_empty_wft() {
|
|
1067
|
+
let wf_name = "update_after_empty_wft";
|
|
1068
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
1069
|
+
let mut worker = starter.worker().await;
|
|
1070
|
+
let client = starter.get_client().await;
|
|
1071
|
+
|
|
1072
|
+
static ACT_STARTED: AtomicBool = AtomicBool::new(false);
|
|
1073
|
+
worker.register_wf(wf_name.to_owned(), move |ctx: WfContext| async move {
|
|
1074
|
+
ctx.update_handler(
|
|
1075
|
+
"update",
|
|
1076
|
+
|_: &_, _: ()| Ok(()),
|
|
1077
|
+
move |ctx: UpdateContext, _: ()| async move {
|
|
1078
|
+
if ACT_STARTED.load(Ordering::Acquire) {
|
|
1079
|
+
return Ok(());
|
|
1080
|
+
}
|
|
1081
|
+
ctx.wf_ctx
|
|
1082
|
+
.activity(ActivityOptions {
|
|
1083
|
+
activity_type: "echo".to_string(),
|
|
1084
|
+
input: "hi!".as_json_payload().expect("serializes fine"),
|
|
1085
|
+
start_to_close_timeout: Some(Duration::from_secs(2)),
|
|
1086
|
+
..Default::default()
|
|
1087
|
+
})
|
|
1088
|
+
.await;
|
|
1089
|
+
Ok(())
|
|
1090
|
+
},
|
|
1091
|
+
);
|
|
1092
|
+
let mut sig = ctx.make_signal_channel("signal");
|
|
1093
|
+
let sig_handle = async {
|
|
1094
|
+
sig.next().await;
|
|
1095
|
+
ACT_STARTED.store(true, Ordering::Release);
|
|
1096
|
+
ctx.activity(ActivityOptions {
|
|
1097
|
+
activity_type: "echo".to_string(),
|
|
1098
|
+
input: "hi!".as_json_payload().expect("serializes fine"),
|
|
1099
|
+
start_to_close_timeout: Some(Duration::from_secs(2)),
|
|
1100
|
+
..Default::default()
|
|
1101
|
+
})
|
|
1102
|
+
.await;
|
|
1103
|
+
ACT_STARTED.store(false, Ordering::Release);
|
|
1104
|
+
};
|
|
1105
|
+
join!(sig_handle, async {
|
|
1106
|
+
ctx.timer(Duration::from_secs(2)).await;
|
|
1107
|
+
});
|
|
1108
|
+
Ok(().into())
|
|
1109
|
+
});
|
|
1110
|
+
worker.register_activity("echo", |_ctx: ActContext, echo_me: String| async move {
|
|
1111
|
+
Ok(echo_me)
|
|
1112
|
+
});
|
|
1113
|
+
|
|
1114
|
+
let run_id = starter.start_with_worker(wf_name, &mut worker).await;
|
|
1115
|
+
let wf_id = starter.get_task_queue().to_string();
|
|
1116
|
+
let update = async {
|
|
1117
|
+
client
|
|
1118
|
+
.signal_workflow_execution(
|
|
1119
|
+
wf_id.clone(),
|
|
1120
|
+
"".to_string(),
|
|
1121
|
+
"signal".to_string(),
|
|
1122
|
+
None,
|
|
1123
|
+
None,
|
|
1124
|
+
)
|
|
1125
|
+
.await
|
|
1126
|
+
.unwrap();
|
|
1127
|
+
tokio::time::sleep(Duration::from_millis(500)).await;
|
|
1128
|
+
let res = client
|
|
1129
|
+
.update_workflow_execution(
|
|
1130
|
+
wf_id.clone(),
|
|
1131
|
+
"".to_string(),
|
|
1132
|
+
"update".to_string(),
|
|
1133
|
+
WaitPolicy {
|
|
1134
|
+
lifecycle_stage: UpdateWorkflowExecutionLifecycleStage::Completed as i32,
|
|
1135
|
+
},
|
|
1136
|
+
[().as_json_payload().unwrap()].into_payloads(),
|
|
1137
|
+
)
|
|
1138
|
+
.await
|
|
1139
|
+
.unwrap();
|
|
1140
|
+
assert!(res.outcome.unwrap().is_success());
|
|
1141
|
+
};
|
|
1142
|
+
let runner = async {
|
|
1143
|
+
worker.run_until_done().await.unwrap();
|
|
1144
|
+
};
|
|
1145
|
+
join!(update, runner);
|
|
1146
|
+
starter
|
|
1147
|
+
.fetch_history_and_replay(wf_id, run_id, worker.inner_mut())
|
|
1148
|
+
.await
|
|
1149
|
+
.unwrap();
|
|
1150
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
use std::cell::Cell;
|
|
2
|
-
use std::sync::Arc;
|
|
3
|
-
|
|
4
1
|
use assert_matches::assert_matches;
|
|
2
|
+
use std::{cell::Cell, sync::Arc, time::Duration};
|
|
5
3
|
use temporal_client::WorkflowOptions;
|
|
6
|
-
use temporal_sdk::interceptors::WorkerInterceptor;
|
|
7
|
-
use temporal_sdk_core::{init_worker, CoreRuntime};
|
|
4
|
+
use temporal_sdk::{interceptors::WorkerInterceptor, WfContext};
|
|
5
|
+
use temporal_sdk_core::{init_worker, CoreRuntime, ResourceBasedTuner, ResourceSlotOptions};
|
|
8
6
|
use temporal_sdk_core_api::{errors::WorkerValidationError, worker::WorkerConfigBuilder, Worker};
|
|
9
|
-
use temporal_sdk_core_protos::
|
|
10
|
-
|
|
7
|
+
use temporal_sdk_core_protos::{
|
|
8
|
+
coresdk::workflow_completion::{
|
|
9
|
+
workflow_activation_completion::Status, Failure, WorkflowActivationCompletion,
|
|
10
|
+
},
|
|
11
|
+
temporal::api::failure::v1::Failure as InnerFailure,
|
|
11
12
|
};
|
|
12
|
-
use temporal_sdk_core_protos::temporal::api::failure::v1::Failure as InnerFailure;
|
|
13
13
|
use temporal_sdk_core_test_utils::{
|
|
14
14
|
drain_pollers_and_shutdown, get_integ_server_options, get_integ_telem_options, CoreWfStarter,
|
|
15
15
|
};
|
|
@@ -111,3 +111,39 @@ async fn worker_handles_unknown_workflow_types_gracefully() {
|
|
|
111
111
|
drain_pollers_and_shutdown(&worker).await;
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
#[tokio::test]
|
|
116
|
+
async fn resource_based_few_pollers_guarantees_non_sticky_poll() {
|
|
117
|
+
let wf_name = "resource_based_few_pollers_guarantees_non_sticky_poll";
|
|
118
|
+
let mut starter = CoreWfStarter::new(wf_name);
|
|
119
|
+
starter
|
|
120
|
+
.worker_config
|
|
121
|
+
.clear_max_outstanding_opts()
|
|
122
|
+
.no_remote_activities(true)
|
|
123
|
+
// 3 pollers so the minimum slots of 2 can both be handed out to a sticky poller
|
|
124
|
+
.max_concurrent_wft_polls(3_usize);
|
|
125
|
+
// Set the limits to zero so it's essentially unwilling to hand out slots
|
|
126
|
+
let mut tuner = ResourceBasedTuner::new(0.0, 0.0);
|
|
127
|
+
tuner.with_workflow_slots_options(ResourceSlotOptions::new(2, 10, Duration::from_millis(0)));
|
|
128
|
+
starter.worker_config.tuner(Arc::new(tuner));
|
|
129
|
+
let mut worker = starter.worker().await;
|
|
130
|
+
|
|
131
|
+
// Workflow doesn't actually need to do anything. We just need to see that we don't get stuck
|
|
132
|
+
// by assigning all slots to sticky pollers.
|
|
133
|
+
worker.register_wf(
|
|
134
|
+
wf_name.to_owned(),
|
|
135
|
+
|_: WfContext| async move { Ok(().into()) },
|
|
136
|
+
);
|
|
137
|
+
for i in 0..20 {
|
|
138
|
+
worker
|
|
139
|
+
.submit_wf(
|
|
140
|
+
format!("{wf_name}_{i}"),
|
|
141
|
+
wf_name.to_owned(),
|
|
142
|
+
vec![],
|
|
143
|
+
WorkflowOptions::default(),
|
|
144
|
+
)
|
|
145
|
+
.await
|
|
146
|
+
.unwrap();
|
|
147
|
+
}
|
|
148
|
+
worker.run_until_done().await.unwrap();
|
|
149
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
use std::collections::HashMap;
|
|
2
2
|
|
|
3
|
-
use
|
|
3
|
+
use futures_util::StreamExt;
|
|
4
4
|
use temporal_client::{SignalWithStartOptions, WorkflowClientTrait, WorkflowOptions};
|
|
5
5
|
use temporal_sdk::{
|
|
6
6
|
ChildWorkflowOptions, Signal, SignalWorkflowOptions, WfContext, WorkflowResult,
|
|
@@ -18,7 +18,8 @@ mod upsert_search_attrs;
|
|
|
18
18
|
|
|
19
19
|
use crate::integ_tests::{activity_functions::echo, metrics_tests};
|
|
20
20
|
use assert_matches::assert_matches;
|
|
21
|
-
use
|
|
21
|
+
use futures_channel::mpsc::UnboundedReceiver;
|
|
22
|
+
use futures_util::{future, SinkExt, StreamExt};
|
|
22
23
|
use std::{
|
|
23
24
|
collections::{HashMap, HashSet},
|
|
24
25
|
sync::{
|
|
@@ -92,7 +93,7 @@ async fn parallel_workflows_same_queue() {
|
|
|
92
93
|
let handles: Vec<_> = run_ids
|
|
93
94
|
.iter()
|
|
94
95
|
.map(|run_id| {
|
|
95
|
-
let (tx, rx) =
|
|
96
|
+
let (tx, rx) = futures_channel::mpsc::unbounded();
|
|
96
97
|
send_chans.insert(run_id.clone(), tx);
|
|
97
98
|
tokio::spawn(wf_task(core.clone(), rx))
|
|
98
99
|
})
|