@temporalio/core-bridge 1.10.3 → 1.11.1
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 +563 -676
- package/Cargo.toml +3 -3
- package/lib/index.d.ts +16 -5
- package/lib/index.js.map +1 -1
- package/lib/worker-tuner.d.ts +57 -0
- package/lib/worker-tuner.js +3 -0
- package/lib/worker-tuner.js.map +1 -0
- 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/heavy.yml +6 -11
- package/sdk-core/.github/workflows/per-pr.yml +23 -41
- package/sdk-core/Cargo.toml +5 -5
- package/sdk-core/README.md +2 -0
- package/sdk-core/client/Cargo.toml +4 -2
- package/sdk-core/client/src/lib.rs +60 -17
- package/sdk-core/client/src/metrics.rs +1 -1
- package/sdk-core/client/src/proxy.rs +17 -12
- package/sdk-core/client/src/raw.rs +218 -69
- package/sdk-core/client/src/retry.rs +19 -9
- package/sdk-core/core/Cargo.toml +12 -12
- package/sdk-core/core/src/abstractions.rs +3 -3
- package/sdk-core/core/src/core_tests/activity_tasks.rs +2 -1
- package/sdk-core/core/src/core_tests/determinism.rs +1 -1
- package/sdk-core/core/src/core_tests/local_activities.rs +73 -10
- package/sdk-core/core/src/core_tests/queries.rs +2 -1
- package/sdk-core/core/src/core_tests/updates.rs +162 -4
- package/sdk-core/core/src/core_tests/workers.rs +38 -2
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +158 -27
- package/sdk-core/core/src/internal_flags.rs +17 -7
- package/sdk-core/core/src/lib.rs +9 -3
- package/sdk-core/core/src/pollers/poll_buffer.rs +1 -10
- package/sdk-core/core/src/protosext/mod.rs +0 -1
- package/sdk-core/core/src/protosext/protocol_messages.rs +105 -16
- package/sdk-core/core/src/retry_logic.rs +22 -2
- package/sdk-core/core/src/telemetry/otel.rs +44 -12
- package/sdk-core/core/src/test_help/mod.rs +65 -12
- package/sdk-core/core/src/worker/activities/local_activities.rs +1 -4
- package/sdk-core/core/src/worker/activities.rs +3 -4
- package/sdk-core/core/src/worker/client/mocks.rs +7 -6
- package/sdk-core/core/src/worker/client.rs +11 -2
- package/sdk-core/core/src/worker/mod.rs +49 -24
- package/sdk-core/core/src/worker/tuner/resource_based.rs +48 -48
- package/sdk-core/core/src/worker/tuner.rs +124 -4
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +1 -1
- package/sdk-core/core/src/worker/workflow/history_update.rs +11 -2
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +18 -3
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +4 -4
- package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +3 -1
- package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +38 -28
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +4 -2
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +95 -71
- package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +1 -1
- package/sdk-core/core/src/worker/workflow/managed_run.rs +214 -14
- package/sdk-core/core/src/worker/workflow/mod.rs +49 -36
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +1 -2
- package/sdk-core/core-api/src/errors.rs +13 -7
- package/sdk-core/core-api/src/lib.rs +9 -1
- package/sdk-core/sdk/Cargo.toml +1 -1
- package/sdk-core/sdk/src/activity_context.rs +3 -4
- package/sdk-core/sdk/src/lib.rs +96 -49
- package/sdk-core/sdk/src/workflow_context/options.rs +8 -4
- package/sdk-core/sdk/src/workflow_context.rs +53 -49
- package/sdk-core/sdk/src/workflow_future.rs +10 -4
- package/sdk-core/sdk-core-protos/Cargo.toml +4 -3
- package/sdk-core/sdk-core-protos/build.rs +2 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/.github/workflows/build.yaml +18 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/LICENSE +21 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/Makefile +59 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/README.md +25 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.gen.yaml +14 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.lock +8 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.yaml +9 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +520 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +263 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +173 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +164 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +36 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/region/v1/message.proto +22 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +50 -8
- package/sdk-core/sdk-core-protos/protos/api_upstream/.gitmodules +3 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +132 -54
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +177 -81
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +13 -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/task_queue.proto +8 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +10 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +3 -3
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +6 -6
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/enhanced_stack_trace.proto +96 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/user_metadata.proto +49 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/workflow_metadata.proto +6 -7
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +55 -24
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +7 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +21 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +64 -45
- package/sdk-core/sdk-core-protos/src/history_builder.rs +8 -1
- package/sdk-core/sdk-core-protos/src/lib.rs +40 -10
- package/sdk-core/test-utils/src/canned_histories.rs +1 -1
- package/sdk-core/tests/fuzzy_workflow.rs +4 -2
- package/sdk-core/tests/heavy_tests.rs +3 -3
- package/sdk-core/tests/integ_tests/activity_functions.rs +2 -2
- package/sdk-core/tests/integ_tests/client_tests.rs +234 -6
- package/sdk-core/tests/integ_tests/update_tests.rs +180 -47
- package/sdk-core/tests/integ_tests/worker_tests.rs +32 -0
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +47 -3
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +10 -10
- package/sdk-core/tests/main.rs +1 -0
- package/src/conversions.rs +84 -11
- package/src/runtime.rs +5 -17
- package/src/worker.rs +27 -6
- package/ts/index.ts +24 -5
- package/ts/worker-tuner.ts +71 -0
|
@@ -146,16 +146,26 @@ where
|
|
|
146
146
|
throttle_backoff: throttle_cfg.into_exp_backoff(throttle_clock),
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
-
|
|
149
|
+
|
|
150
|
+
fn maybe_log_retry(&self, cur_attempt: usize, err: &tonic::Status) {
|
|
151
|
+
let mut do_log = false;
|
|
150
152
|
// Warn on more than 5 retries for unlimited retrying
|
|
151
153
|
if self.max_retries == 0 && cur_attempt > 5 {
|
|
152
|
-
|
|
154
|
+
do_log = true;
|
|
153
155
|
}
|
|
154
156
|
// Warn if the attempts are more than 50% of max retries
|
|
155
157
|
if self.max_retries > 0 && cur_attempt * 2 >= self.max_retries {
|
|
156
|
-
|
|
158
|
+
do_log = true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if do_log {
|
|
162
|
+
// Error if unlimited retries have been going on for a while
|
|
163
|
+
if self.max_retries == 0 && cur_attempt > 15 {
|
|
164
|
+
error!(error=?err, "gRPC call {} retried {} times", self.call_name, cur_attempt);
|
|
165
|
+
} else {
|
|
166
|
+
warn!(error=?err, "gRPC call {} retried {} times", self.call_name, cur_attempt);
|
|
167
|
+
}
|
|
157
168
|
}
|
|
158
|
-
false
|
|
159
169
|
}
|
|
160
170
|
}
|
|
161
171
|
#[doc(hidden)]
|
|
@@ -194,8 +204,8 @@ where
|
|
|
194
204
|
if RETRYABLE_ERROR_CODES.contains(&e.code()) || long_poll_allowed {
|
|
195
205
|
if current_attempt == 1 {
|
|
196
206
|
debug!(error=?e, "gRPC call {} failed on first attempt", self.call_name);
|
|
197
|
-
} else
|
|
198
|
-
|
|
207
|
+
} else {
|
|
208
|
+
self.maybe_log_retry(current_attempt, &e);
|
|
199
209
|
}
|
|
200
210
|
|
|
201
211
|
match self.backoff.next_backoff() {
|
|
@@ -471,7 +481,7 @@ where
|
|
|
471
481
|
list_open_workflow_executions,
|
|
472
482
|
maximum_page_size,
|
|
473
483
|
next_page_token.clone(),
|
|
474
|
-
start_time_filter
|
|
484
|
+
start_time_filter,
|
|
475
485
|
filters.clone()
|
|
476
486
|
)
|
|
477
487
|
}
|
|
@@ -488,7 +498,7 @@ where
|
|
|
488
498
|
list_closed_workflow_executions,
|
|
489
499
|
maximum_page_size,
|
|
490
500
|
next_page_token.clone(),
|
|
491
|
-
start_time_filter
|
|
501
|
+
start_time_filter,
|
|
492
502
|
filters.clone()
|
|
493
503
|
)
|
|
494
504
|
}
|
|
@@ -541,7 +551,7 @@ where
|
|
|
541
551
|
workflow_id.clone(),
|
|
542
552
|
run_id.clone(),
|
|
543
553
|
name.clone(),
|
|
544
|
-
wait_policy
|
|
554
|
+
wait_policy,
|
|
545
555
|
args.clone()
|
|
546
556
|
)
|
|
547
557
|
}
|
package/sdk-core/core/Cargo.toml
CHANGED
|
@@ -17,16 +17,16 @@ default = ["otel"]
|
|
|
17
17
|
otel = ["dep:opentelemetry", "dep:opentelemetry_sdk", "dep:opentelemetry-otlp",
|
|
18
18
|
"dep:opentelemetry-prometheus", "dep:hyper", "dep:hyper-util", "dep:http-body-util"]
|
|
19
19
|
tokio-console = ["console-subscriber"]
|
|
20
|
-
ephemeral-server = ["dep:flate2", "dep:
|
|
20
|
+
ephemeral-server = ["dep:flate2", "dep:reqwest", "dep:tar", "dep:zip"]
|
|
21
21
|
|
|
22
22
|
[dependencies]
|
|
23
23
|
anyhow = "1.0"
|
|
24
24
|
async-trait = "0.1"
|
|
25
|
-
console-subscriber = { version = "0.
|
|
25
|
+
console-subscriber = { version = "0.4", optional = true }
|
|
26
26
|
crossbeam-channel = "0.5"
|
|
27
27
|
crossbeam-queue = "0.3"
|
|
28
28
|
crossbeam-utils = "0.8"
|
|
29
|
-
dashmap = "
|
|
29
|
+
dashmap = "6.0"
|
|
30
30
|
derive_builder = { workspace = true }
|
|
31
31
|
derive_more = { workspace = true }
|
|
32
32
|
enum_dispatch = "0.3"
|
|
@@ -38,23 +38,22 @@ governor = "0.6"
|
|
|
38
38
|
http-body-util = { version = "0.1", optional = true }
|
|
39
39
|
hyper = { version = "1.2", optional = true }
|
|
40
40
|
hyper-util = { version = "0.1", features = ["server", "http1", "http2", "tokio"], optional = true }
|
|
41
|
-
itertools = "0.
|
|
41
|
+
itertools = "0.13"
|
|
42
42
|
lru = "0.12"
|
|
43
43
|
mockall = "0.12"
|
|
44
|
-
nix = { version = "0.28", optional = true, features = ["process", "signal"] }
|
|
45
44
|
once_cell = { workspace = true }
|
|
46
45
|
opentelemetry = { workspace = true, features = ["metrics"], optional = true }
|
|
47
|
-
opentelemetry_sdk = { version = "0.
|
|
48
|
-
opentelemetry-otlp = { version = "0.
|
|
49
|
-
opentelemetry-prometheus = { version = "0.
|
|
46
|
+
opentelemetry_sdk = { version = "0.24", features = ["rt-tokio", "metrics"], optional = true }
|
|
47
|
+
opentelemetry-otlp = { version = "0.17", features = ["tokio", "metrics"], optional = true }
|
|
48
|
+
opentelemetry-prometheus = { version = "0.17", optional = true }
|
|
50
49
|
parking_lot = { version = "0.12", features = ["send_guard"] }
|
|
51
50
|
pid = "4.0"
|
|
52
51
|
pin-project = "1.0"
|
|
53
52
|
prometheus = "0.13"
|
|
54
53
|
prost = { workspace = true }
|
|
55
|
-
prost-types = { version = "0.
|
|
54
|
+
prost-types = { version = "0.6", package = "prost-wkt-types" }
|
|
56
55
|
rand = "0.8.3"
|
|
57
|
-
reqwest = { version = "0.
|
|
56
|
+
reqwest = { version = "0.12", features = ["json", "stream", "rustls-tls"], default-features = false, optional = true }
|
|
58
57
|
ringbuf = "0.4"
|
|
59
58
|
serde = "1.0"
|
|
60
59
|
serde_json = "1.0"
|
|
@@ -71,7 +70,7 @@ tracing = "0.1"
|
|
|
71
70
|
tracing-subscriber = { version = "0.3", features = ["parking_lot", "env-filter", "registry"] }
|
|
72
71
|
url = "2.2"
|
|
73
72
|
uuid = { version = "1.1", features = ["v4"] }
|
|
74
|
-
zip = { version = "
|
|
73
|
+
zip = { version = "2.0", optional = true }
|
|
75
74
|
|
|
76
75
|
# 1st party local deps
|
|
77
76
|
[dependencies.temporal-sdk-core-api]
|
|
@@ -93,10 +92,11 @@ assert_matches = "1.4"
|
|
|
93
92
|
bimap = "0.6.1"
|
|
94
93
|
clap = { version = "4.0", features = ["derive"] }
|
|
95
94
|
criterion = "0.5"
|
|
96
|
-
rstest = "0.
|
|
95
|
+
rstest = "0.21"
|
|
97
96
|
sysinfo = "0.30"
|
|
98
97
|
temporal-sdk-core-test-utils = { path = "../test-utils" }
|
|
99
98
|
temporal-sdk = { path = "../sdk" }
|
|
99
|
+
tokio-stream = { version = "0.1", features = ["net"] }
|
|
100
100
|
|
|
101
101
|
[build-dependencies]
|
|
102
102
|
tonic-build = { workspace = true }
|
|
@@ -66,7 +66,7 @@ where
|
|
|
66
66
|
.map(|ap| ap + self.unused_claimants.load(Ordering::Acquire))
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
pub(crate) async fn acquire_owned(&self) ->
|
|
69
|
+
pub(crate) async fn acquire_owned(&self) -> OwnedMeteredSemPermit<SK> {
|
|
70
70
|
if let Some(max) = self.max_permits {
|
|
71
71
|
self.extant_permits
|
|
72
72
|
.1
|
|
@@ -76,7 +76,7 @@ where
|
|
|
76
76
|
.expect("Extant permit channel is never closed");
|
|
77
77
|
}
|
|
78
78
|
let res = self.supplier.reserve_slot(self).await;
|
|
79
|
-
|
|
79
|
+
self.build_owned(res)
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
pub(crate) fn try_acquire_owned(&self) -> Result<OwnedMeteredSemPermit<SK>, ()> {
|
|
@@ -351,6 +351,6 @@ pub(crate) mod tests {
|
|
|
351
351
|
advance_fut!(acquire_fut);
|
|
352
352
|
drop(perm);
|
|
353
353
|
// Now it'll proceed
|
|
354
|
-
acquire_fut.await
|
|
354
|
+
acquire_fut.await;
|
|
355
355
|
}
|
|
356
356
|
}
|
|
@@ -900,7 +900,7 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
900
900
|
],
|
|
901
901
|
mock,
|
|
902
902
|
);
|
|
903
|
-
mh.
|
|
903
|
+
mh.completion_mock_fn = Some(Box::new(|wftc| {
|
|
904
904
|
// Make sure when we see the completion with the schedule act command that it does
|
|
905
905
|
// not have the eager execution flag set the first time, and does the second.
|
|
906
906
|
if let Some(Attributes::ScheduleActivityTaskCommandAttributes(attrs)) = wftc
|
|
@@ -914,6 +914,7 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
914
914
|
assert!(attrs.request_eager_execution);
|
|
915
915
|
}
|
|
916
916
|
}
|
|
917
|
+
Ok(Default::default())
|
|
917
918
|
}));
|
|
918
919
|
let mut mock = build_mock_pollers(mh);
|
|
919
920
|
mock.worker_cfg(|cfg| {
|
|
@@ -125,7 +125,7 @@ async fn activity_id_or_type_change_is_nondeterministic(
|
|
|
125
125
|
) {
|
|
126
126
|
let wf_id = "fakeid";
|
|
127
127
|
let wf_type = DEFAULT_WORKFLOW_TYPE;
|
|
128
|
-
let mut t = if local_act {
|
|
128
|
+
let mut t: TestHistoryBuilder = if local_act {
|
|
129
129
|
canned_histories::single_local_activity("1")
|
|
130
130
|
} else {
|
|
131
131
|
canned_histories::single_activity("1")
|
|
@@ -17,12 +17,11 @@ use std::{
|
|
|
17
17
|
atomic::{AtomicUsize, Ordering},
|
|
18
18
|
Arc,
|
|
19
19
|
},
|
|
20
|
-
time::{Duration, SystemTime},
|
|
20
|
+
time::{Duration, Instant, SystemTime},
|
|
21
21
|
};
|
|
22
22
|
use temporal_client::WorkflowOptions;
|
|
23
23
|
use temporal_sdk::{
|
|
24
|
-
ActContext,
|
|
25
|
-
WorkflowResult,
|
|
24
|
+
ActContext, ActivityError, LocalActivityOptions, WfContext, WorkflowFunction, WorkflowResult,
|
|
26
25
|
};
|
|
27
26
|
use temporal_sdk_core_api::{
|
|
28
27
|
errors::{PollActivityError, PollWfError},
|
|
@@ -49,7 +48,7 @@ use temporal_sdk_core_test_utils::{
|
|
|
49
48
|
};
|
|
50
49
|
use tokio::{join, select, sync::Barrier};
|
|
51
50
|
|
|
52
|
-
async fn echo(_ctx: ActContext, e: String) ->
|
|
51
|
+
async fn echo(_ctx: ActContext, e: String) -> Result<String, ActivityError> {
|
|
53
52
|
Ok(e)
|
|
54
53
|
}
|
|
55
54
|
|
|
@@ -279,7 +278,7 @@ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
|
|
|
279
278
|
if 2 == attempts.fetch_add(1, Ordering::Relaxed) && eventually_pass {
|
|
280
279
|
Ok(())
|
|
281
280
|
} else {
|
|
282
|
-
Err(anyhow!("Oh no I failed!"))
|
|
281
|
+
Err(anyhow!("Oh no I failed!").into())
|
|
283
282
|
}
|
|
284
283
|
});
|
|
285
284
|
worker
|
|
@@ -356,7 +355,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
|
|
|
356
355
|
worker.register_activity(
|
|
357
356
|
DEFAULT_ACTIVITY_TYPE,
|
|
358
357
|
move |_ctx: ActContext, _: String| async move {
|
|
359
|
-
Result::<(), _>::Err(anyhow!("Oh no I failed!"))
|
|
358
|
+
Result::<(), _>::Err(anyhow!("Oh no I failed!").into())
|
|
360
359
|
},
|
|
361
360
|
);
|
|
362
361
|
worker
|
|
@@ -928,7 +927,7 @@ async fn start_to_close_timeout_allows_retries(#[values(true, false)] la_complet
|
|
|
928
927
|
_ = tokio::time::sleep(Duration::from_millis(100)) => (),
|
|
929
928
|
_ = ctx.cancelled() => {
|
|
930
929
|
cancels.fetch_add(1, Ordering::AcqRel);
|
|
931
|
-
return Err(
|
|
930
|
+
return Err(ActivityError::cancelled());
|
|
932
931
|
}
|
|
933
932
|
}
|
|
934
933
|
}
|
|
@@ -991,7 +990,7 @@ async fn wft_failure_cancels_running_las() {
|
|
|
991
990
|
if res.is_err() {
|
|
992
991
|
panic!("Activity must be cancelled!!!!");
|
|
993
992
|
}
|
|
994
|
-
Result::<(), _>::Err(
|
|
993
|
+
Result::<(), _>::Err(ActivityError::cancelled())
|
|
995
994
|
},
|
|
996
995
|
);
|
|
997
996
|
worker
|
|
@@ -1076,11 +1075,12 @@ async fn local_act_records_nonfirst_attempts_ok() {
|
|
|
1076
1075
|
let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
|
|
1077
1076
|
let nonfirst_counts = Arc::new(SegQueue::new());
|
|
1078
1077
|
let nfc_c = nonfirst_counts.clone();
|
|
1079
|
-
mh.
|
|
1078
|
+
mh.completion_mock_fn = Some(Box::new(move |c| {
|
|
1080
1079
|
nfc_c.push(
|
|
1081
1080
|
c.metering_metadata
|
|
1082
1081
|
.nonfirst_local_activity_execution_attempts,
|
|
1083
1082
|
);
|
|
1083
|
+
Ok(Default::default())
|
|
1084
1084
|
}));
|
|
1085
1085
|
let mut worker = mock_sdk_cfg(mh, |wc| {
|
|
1086
1086
|
wc.max_cached_workflows = 1;
|
|
@@ -1107,7 +1107,7 @@ async fn local_act_records_nonfirst_attempts_ok() {
|
|
|
1107
1107
|
},
|
|
1108
1108
|
);
|
|
1109
1109
|
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
1110
|
-
Result::<(), _>::Err(anyhow!("I fail"))
|
|
1110
|
+
Result::<(), _>::Err(anyhow!("I fail").into())
|
|
1111
1111
|
});
|
|
1112
1112
|
worker
|
|
1113
1113
|
.submit_wf(
|
|
@@ -1390,3 +1390,66 @@ async fn local_activity_after_wf_complete_is_discarded() {
|
|
|
1390
1390
|
join!(wf_poller, at_poller);
|
|
1391
1391
|
core.drain_pollers_and_shutdown().await;
|
|
1392
1392
|
}
|
|
1393
|
+
|
|
1394
|
+
#[tokio::test]
|
|
1395
|
+
async fn local_act_retry_explicit_delay() {
|
|
1396
|
+
let mut t = TestHistoryBuilder::default();
|
|
1397
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
1398
|
+
t.add_workflow_task_scheduled_and_started();
|
|
1399
|
+
|
|
1400
|
+
let wf_id = "fakeid";
|
|
1401
|
+
let mock = mock_workflow_client();
|
|
1402
|
+
let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
|
|
1403
|
+
let mut worker = mock_sdk(mh);
|
|
1404
|
+
|
|
1405
|
+
worker.register_wf(
|
|
1406
|
+
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1407
|
+
move |ctx: WfContext| async move {
|
|
1408
|
+
let la_res = ctx
|
|
1409
|
+
.local_activity(LocalActivityOptions {
|
|
1410
|
+
activity_type: "echo".to_string(),
|
|
1411
|
+
input: "hi".as_json_payload().expect("serializes fine"),
|
|
1412
|
+
retry_policy: RetryPolicy {
|
|
1413
|
+
initial_interval: Some(prost_dur!(from_millis(50))),
|
|
1414
|
+
backoff_coefficient: 1.0,
|
|
1415
|
+
maximum_attempts: 5,
|
|
1416
|
+
..Default::default()
|
|
1417
|
+
},
|
|
1418
|
+
..Default::default()
|
|
1419
|
+
})
|
|
1420
|
+
.await;
|
|
1421
|
+
assert!(la_res.completed_ok());
|
|
1422
|
+
Ok(().into())
|
|
1423
|
+
},
|
|
1424
|
+
);
|
|
1425
|
+
let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
|
|
1426
|
+
worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
|
|
1427
|
+
// Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
|
|
1428
|
+
let last_attempt = attempts.fetch_add(1, Ordering::Relaxed);
|
|
1429
|
+
if 0 == last_attempt {
|
|
1430
|
+
Err(ActivityError::Retryable {
|
|
1431
|
+
source: anyhow!("Explicit backoff error"),
|
|
1432
|
+
explicit_delay: Some(Duration::from_millis(300)),
|
|
1433
|
+
})
|
|
1434
|
+
} else if 2 == last_attempt {
|
|
1435
|
+
Ok(())
|
|
1436
|
+
} else {
|
|
1437
|
+
Err(anyhow!("Oh no I failed!").into())
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
worker
|
|
1441
|
+
.submit_wf(
|
|
1442
|
+
wf_id.to_owned(),
|
|
1443
|
+
DEFAULT_WORKFLOW_TYPE.to_owned(),
|
|
1444
|
+
vec![],
|
|
1445
|
+
WorkflowOptions::default(),
|
|
1446
|
+
)
|
|
1447
|
+
.await
|
|
1448
|
+
.unwrap();
|
|
1449
|
+
let start = Instant::now();
|
|
1450
|
+
worker.run_until_done().await.unwrap();
|
|
1451
|
+
let expected_attempts = 3;
|
|
1452
|
+
assert_eq!(expected_attempts, attempts.load(Ordering::Relaxed));
|
|
1453
|
+
// There will be one 300ms backoff and one 50s backoff, so things should take at least that long
|
|
1454
|
+
assert!(start.elapsed() > Duration::from_millis(350));
|
|
1455
|
+
}
|
|
@@ -167,7 +167,7 @@ async fn new_queries(#[values(1, 3)] num_queries: usize) {
|
|
|
167
167
|
let mut mock_client = mock_workflow_client();
|
|
168
168
|
mock_client.expect_respond_legacy_query().times(0);
|
|
169
169
|
let mut mh = MockPollCfg::from_resp_batches(wfid, t, tasks, mock_workflow_client());
|
|
170
|
-
mh.
|
|
170
|
+
mh.completion_mock_fn = Some(Box::new(move |c| {
|
|
171
171
|
// If the completion is the one ending the workflow, make sure it includes the query resps
|
|
172
172
|
if c.commands[0].command_type() == CommandType::CompleteWorkflowExecution {
|
|
173
173
|
assert_eq!(c.query_responses.len(), num_queries);
|
|
@@ -176,6 +176,7 @@ async fn new_queries(#[values(1, 3)] num_queries: usize) {
|
|
|
176
176
|
} else {
|
|
177
177
|
panic!("Unexpected command in response")
|
|
178
178
|
}
|
|
179
|
+
Ok(Default::default())
|
|
179
180
|
}));
|
|
180
181
|
let mut mock = build_mock_pollers(mh);
|
|
181
182
|
mock.worker_cfg(|wc| wc.max_cached_workflows = 10);
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
use crate::
|
|
1
|
+
use crate::{
|
|
2
|
+
test_help::{
|
|
3
|
+
build_mock_pollers, hist_to_poll_resp, mock_worker, MockPollCfg, PollWFTRespExt,
|
|
4
|
+
ResponseType,
|
|
5
|
+
},
|
|
6
|
+
worker::client::mocks::mock_workflow_client,
|
|
7
|
+
};
|
|
2
8
|
use temporal_sdk_core_api::Worker;
|
|
3
9
|
use temporal_sdk_core_protos::{
|
|
4
10
|
coresdk::{
|
|
5
11
|
workflow_activation::{workflow_activation_job, WorkflowActivationJob},
|
|
6
|
-
workflow_commands::{
|
|
12
|
+
workflow_commands::{
|
|
13
|
+
update_response::Response, CompleteWorkflowExecution, ScheduleActivity, UpdateResponse,
|
|
14
|
+
},
|
|
7
15
|
workflow_completion::WorkflowActivationCompletion,
|
|
8
16
|
},
|
|
9
|
-
temporal::api::{
|
|
10
|
-
|
|
17
|
+
temporal::api::{
|
|
18
|
+
common::v1::Payload,
|
|
19
|
+
enums::v1::EventType,
|
|
20
|
+
update::v1::{Acceptance, Rejection},
|
|
21
|
+
workflowservice::v1::RespondWorkflowTaskCompletedResponse,
|
|
22
|
+
},
|
|
23
|
+
TestHistoryBuilder, DEFAULT_ACTIVITY_TYPE,
|
|
11
24
|
};
|
|
25
|
+
use temporal_sdk_core_test_utils::WorkerTestHelpers;
|
|
12
26
|
|
|
13
27
|
#[tokio::test]
|
|
14
28
|
async fn replay_with_empty_first_task() {
|
|
@@ -71,3 +85,147 @@ async fn replay_with_empty_first_task() {
|
|
|
71
85
|
.await
|
|
72
86
|
.unwrap();
|
|
73
87
|
}
|
|
88
|
+
|
|
89
|
+
#[rstest::rstest]
|
|
90
|
+
#[tokio::test]
|
|
91
|
+
async fn initial_request_sent_back(#[values(false, true)] reject: bool) {
|
|
92
|
+
let wfid = "fakeid";
|
|
93
|
+
let mut t = TestHistoryBuilder::default();
|
|
94
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
95
|
+
t.add_workflow_task_scheduled_and_started();
|
|
96
|
+
|
|
97
|
+
let update_id = "upd-1";
|
|
98
|
+
let mut poll_resp = hist_to_poll_resp(&t, wfid, ResponseType::AllHistory);
|
|
99
|
+
let upd_req_body = poll_resp.add_update_request(update_id, 1);
|
|
100
|
+
|
|
101
|
+
let mut mock_client = mock_workflow_client();
|
|
102
|
+
mock_client
|
|
103
|
+
.expect_complete_workflow_task()
|
|
104
|
+
.times(1)
|
|
105
|
+
.returning(move |mut resp| {
|
|
106
|
+
let msg = resp.messages.pop().unwrap();
|
|
107
|
+
let orig_req = if reject {
|
|
108
|
+
let acceptance = msg.body.unwrap().unpack_as(Rejection::default()).unwrap();
|
|
109
|
+
acceptance.rejected_request.unwrap()
|
|
110
|
+
} else {
|
|
111
|
+
let acceptance = msg.body.unwrap().unpack_as(Acceptance::default()).unwrap();
|
|
112
|
+
acceptance.accepted_request.unwrap()
|
|
113
|
+
};
|
|
114
|
+
assert_eq!(orig_req, upd_req_body);
|
|
115
|
+
Ok(RespondWorkflowTaskCompletedResponse::default())
|
|
116
|
+
});
|
|
117
|
+
let mh = MockPollCfg::from_resp_batches(wfid, t, [poll_resp], mock_client);
|
|
118
|
+
let mut mock = build_mock_pollers(mh);
|
|
119
|
+
mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
|
|
120
|
+
let core = mock_worker(mock);
|
|
121
|
+
|
|
122
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
123
|
+
let resp = if reject {
|
|
124
|
+
Response::Rejected(Default::default())
|
|
125
|
+
} else {
|
|
126
|
+
Response::Accepted(())
|
|
127
|
+
};
|
|
128
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
129
|
+
task.run_id,
|
|
130
|
+
UpdateResponse {
|
|
131
|
+
protocol_instance_id: update_id.to_string(),
|
|
132
|
+
response: Some(resp),
|
|
133
|
+
}
|
|
134
|
+
.into(),
|
|
135
|
+
))
|
|
136
|
+
.await
|
|
137
|
+
.unwrap();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#[tokio::test]
|
|
141
|
+
async fn speculative_wft_with_command_event() {
|
|
142
|
+
let wfid = "fakeid";
|
|
143
|
+
let mut t = TestHistoryBuilder::default();
|
|
144
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
145
|
+
t.add_full_wf_task();
|
|
146
|
+
t.add_activity_task_scheduled("act1");
|
|
147
|
+
|
|
148
|
+
let mut spec_task_hist = t.clone();
|
|
149
|
+
spec_task_hist.add_workflow_task_scheduled_and_started();
|
|
150
|
+
|
|
151
|
+
let mut real_hist = t.clone();
|
|
152
|
+
real_hist.add_we_signaled("hi", vec![]);
|
|
153
|
+
real_hist.add_workflow_task_scheduled_and_started();
|
|
154
|
+
|
|
155
|
+
let update_id = "upd-1";
|
|
156
|
+
let mut speculative_task = hist_to_poll_resp(&spec_task_hist, wfid, ResponseType::OneTask(2));
|
|
157
|
+
speculative_task.add_update_request(update_id, 1);
|
|
158
|
+
// Verify the speculative task contains the activity scheduled event
|
|
159
|
+
assert_eq!(
|
|
160
|
+
speculative_task.history.as_ref().unwrap().events[1].event_type,
|
|
161
|
+
EventType::ActivityTaskScheduled as i32
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
let mock_client = mock_workflow_client();
|
|
165
|
+
let mut mh = MockPollCfg::from_resp_batches(
|
|
166
|
+
wfid,
|
|
167
|
+
real_hist,
|
|
168
|
+
[
|
|
169
|
+
ResponseType::ToTaskNum(1),
|
|
170
|
+
speculative_task.into(),
|
|
171
|
+
ResponseType::OneTask(3),
|
|
172
|
+
],
|
|
173
|
+
mock_client,
|
|
174
|
+
);
|
|
175
|
+
let mut completes = 0;
|
|
176
|
+
mh.completion_mock_fn = Some(Box::new(move |_| {
|
|
177
|
+
completes += 1;
|
|
178
|
+
let mut r = RespondWorkflowTaskCompletedResponse::default();
|
|
179
|
+
if completes == 2 {
|
|
180
|
+
// The second response (the update rejection) needs to indicate that the last started
|
|
181
|
+
// wft ID should be reset.
|
|
182
|
+
r.reset_history_event_id = 3;
|
|
183
|
+
}
|
|
184
|
+
Ok(r)
|
|
185
|
+
}));
|
|
186
|
+
let mut mock = build_mock_pollers(mh);
|
|
187
|
+
mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
|
|
188
|
+
let core = mock_worker(mock);
|
|
189
|
+
|
|
190
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
191
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
192
|
+
task.run_id,
|
|
193
|
+
ScheduleActivity {
|
|
194
|
+
activity_id: "act1".to_string(),
|
|
195
|
+
activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
|
|
196
|
+
..Default::default()
|
|
197
|
+
}
|
|
198
|
+
.into(),
|
|
199
|
+
))
|
|
200
|
+
.await
|
|
201
|
+
.unwrap();
|
|
202
|
+
|
|
203
|
+
// Receive the task containing and reject the update
|
|
204
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
205
|
+
assert_matches!(
|
|
206
|
+
task.jobs.as_slice(),
|
|
207
|
+
[WorkflowActivationJob {
|
|
208
|
+
variant: Some(workflow_activation_job::Variant::DoUpdate(_)),
|
|
209
|
+
}]
|
|
210
|
+
);
|
|
211
|
+
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
212
|
+
task.run_id,
|
|
213
|
+
UpdateResponse {
|
|
214
|
+
protocol_instance_id: update_id.to_string(),
|
|
215
|
+
response: Some(Response::Rejected(Default::default())),
|
|
216
|
+
}
|
|
217
|
+
.into(),
|
|
218
|
+
))
|
|
219
|
+
.await
|
|
220
|
+
.unwrap();
|
|
221
|
+
|
|
222
|
+
// Now we'll get another task with the "real" history containing the signal
|
|
223
|
+
let task = core.poll_workflow_activation().await.unwrap();
|
|
224
|
+
assert_matches!(
|
|
225
|
+
task.jobs.as_slice(),
|
|
226
|
+
[WorkflowActivationJob {
|
|
227
|
+
variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
|
|
228
|
+
}]
|
|
229
|
+
);
|
|
230
|
+
core.complete_execution(&task.run_id).await;
|
|
231
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
use crate::{
|
|
2
2
|
prost_dur,
|
|
3
3
|
test_help::{
|
|
4
|
-
build_fake_worker, build_mock_pollers, canned_histories, mock_worker,
|
|
5
|
-
MockWorkerInputs, MocksHolder, ResponseType, WorkerExt,
|
|
4
|
+
build_fake_worker, build_mock_pollers, canned_histories, mock_worker, test_worker_cfg,
|
|
5
|
+
MockPollCfg, MockWorkerInputs, MocksHolder, ResponseType, WorkerExt,
|
|
6
6
|
},
|
|
7
|
+
worker,
|
|
7
8
|
worker::client::mocks::mock_workflow_client,
|
|
8
9
|
PollActivityError, PollWfError,
|
|
9
10
|
};
|
|
@@ -259,3 +260,38 @@ async fn worker_does_not_panic_on_retry_exhaustion_of_nonfatal_net_err() {
|
|
|
259
260
|
Some(workflow_activation_job::Variant::RemoveFromCache(_))
|
|
260
261
|
);
|
|
261
262
|
}
|
|
263
|
+
|
|
264
|
+
#[rstest::rstest]
|
|
265
|
+
#[tokio::test]
|
|
266
|
+
async fn worker_can_shutdown_after_never_polling_ok(#[values(true, false)] poll_workflow: bool) {
|
|
267
|
+
let mut mock = mock_workflow_client();
|
|
268
|
+
mock.expect_poll_activity_task()
|
|
269
|
+
.returning(|_, _| Err(tonic::Status::permission_denied("you shall not pass")));
|
|
270
|
+
if poll_workflow {
|
|
271
|
+
mock.expect_poll_workflow_task()
|
|
272
|
+
.returning(|_| Err(tonic::Status::permission_denied("you shall not pass")));
|
|
273
|
+
}
|
|
274
|
+
let core = worker::Worker::new_test(
|
|
275
|
+
test_worker_cfg()
|
|
276
|
+
.max_concurrent_at_polls(1_usize)
|
|
277
|
+
.build()
|
|
278
|
+
.unwrap(),
|
|
279
|
+
mock,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
loop {
|
|
283
|
+
// Must continue polling until polls return shutdown.
|
|
284
|
+
if poll_workflow {
|
|
285
|
+
let res = core.poll_workflow_activation().await.unwrap_err();
|
|
286
|
+
if !matches!(res, PollWfError::ShutDown) {
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
let res = core.poll_activity_task().await.unwrap_err();
|
|
291
|
+
if !matches!(res, PollActivityError::ShutDown) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
core.finalize_shutdown().await;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|