@temporalio/core-bridge 1.7.4 → 1.8.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 +245 -247
- package/Cargo.toml +1 -1
- package/lib/errors.d.ts +9 -0
- package/lib/errors.js +13 -0
- package/lib/errors.js.map +1 -1
- package/lib/index.d.ts +19 -3
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/sdk-core/.github/workflows/heavy.yml +1 -1
- package/sdk-core/.github/workflows/semgrep.yml +25 -0
- package/sdk-core/README.md +2 -0
- package/sdk-core/cargo-tokio-console.sh +5 -0
- package/sdk-core/client/src/lib.rs +6 -41
- package/sdk-core/client/src/raw.rs +9 -0
- package/sdk-core/client/src/retry.rs +0 -16
- package/sdk-core/core/Cargo.toml +9 -5
- package/sdk-core/core/src/abstractions.rs +7 -75
- package/sdk-core/core/src/core_tests/activity_tasks.rs +16 -8
- package/sdk-core/core/src/core_tests/local_activities.rs +97 -5
- package/sdk-core/core/src/core_tests/mod.rs +1 -1
- package/sdk-core/core/src/core_tests/workers.rs +16 -16
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +247 -28
- package/sdk-core/core/src/lib.rs +2 -3
- package/sdk-core/core/src/pollers/mod.rs +30 -3
- package/sdk-core/core/src/pollers/poll_buffer.rs +166 -77
- package/sdk-core/core/src/protosext/mod.rs +4 -8
- package/sdk-core/core/src/replay/mod.rs +1 -1
- package/sdk-core/core/src/telemetry/metrics.rs +9 -0
- package/sdk-core/core/src/telemetry/mod.rs +3 -0
- package/sdk-core/core/src/test_help/mod.rs +9 -16
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +6 -31
- package/sdk-core/core/src/worker/activities/local_activities.rs +214 -110
- package/sdk-core/core/src/worker/activities.rs +72 -47
- package/sdk-core/core/src/worker/client/mocks.rs +1 -1
- package/sdk-core/core/src/worker/client.rs +45 -32
- package/sdk-core/core/src/worker/mod.rs +170 -122
- package/sdk-core/core/src/worker/workflow/driven_workflow.rs +0 -4
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +9 -2
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +9 -2
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +6 -3
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +74 -22
- package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +3 -2
- package/sdk-core/core/src/worker/workflow/managed_run.rs +16 -3
- package/sdk-core/core/src/worker/workflow/mod.rs +13 -22
- package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -0
- package/sdk-core/core/src/worker/workflow/wft_extraction.rs +4 -7
- package/sdk-core/core/src/worker/workflow/wft_poller.rs +38 -8
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +1 -0
- package/sdk-core/core-api/src/worker.rs +43 -2
- package/sdk-core/protos/api_upstream/Makefile +1 -1
- package/sdk-core/protos/api_upstream/buf.yaml +1 -6
- package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +12 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +9 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +19 -0
- package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +5 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +36 -4
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +24 -7
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +4 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +76 -44
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +23 -1
- package/sdk-core/protos/google/rpc/status.proto +52 -0
- package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +16 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -0
- package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +6 -0
- package/sdk-core/sdk/src/lib.rs +31 -10
- package/sdk-core/sdk/src/workflow_future.rs +7 -5
- package/sdk-core/sdk-core-protos/src/history_builder.rs +2 -0
- package/sdk-core/sdk-core-protos/src/history_info.rs +1 -0
- package/sdk-core/sdk-core-protos/src/lib.rs +82 -73
- package/sdk-core/test-utils/Cargo.toml +1 -1
- package/sdk-core/test-utils/src/lib.rs +50 -37
- package/sdk-core/tests/integ_tests/metrics_tests.rs +143 -10
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +26 -15
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +2 -2
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +5 -1
- package/sdk-core/tests/integ_tests/workflow_tests.rs +1 -0
- package/src/conversions.rs +9 -2
- package/src/runtime.rs +5 -7
- package/ts/errors.ts +15 -0
- package/ts/index.ts +22 -4
|
@@ -47,7 +47,7 @@ use std::{
|
|
|
47
47
|
};
|
|
48
48
|
use temporal_sdk_core_protos::{
|
|
49
49
|
coresdk::{
|
|
50
|
-
common::NamespacedWorkflowExecution,
|
|
50
|
+
common::{NamespacedWorkflowExecution, VersioningIntent},
|
|
51
51
|
workflow_activation,
|
|
52
52
|
workflow_activation::{
|
|
53
53
|
workflow_activation_job, NotifyHasPatch, UpdateRandomSeed, WorkflowActivation,
|
|
@@ -95,6 +95,11 @@ pub(crate) struct WorkflowMachines {
|
|
|
95
95
|
pub workflow_type: String,
|
|
96
96
|
/// Identifies the current run
|
|
97
97
|
pub run_id: String,
|
|
98
|
+
/// The task queue this workflow is operating within
|
|
99
|
+
pub task_queue: String,
|
|
100
|
+
/// Is set to true once we've seen the final event in workflow history, to avoid accidentally
|
|
101
|
+
/// re-applying the final workflow task.
|
|
102
|
+
pub have_seen_terminal_event: bool,
|
|
98
103
|
/// The time the workflow execution began, as told by the WEStarted event
|
|
99
104
|
workflow_start_time: Option<SystemTime>,
|
|
100
105
|
/// The time the workflow execution finished, as determined by when the machines handled
|
|
@@ -108,6 +113,10 @@ pub(crate) struct WorkflowMachines {
|
|
|
108
113
|
/// The internal flags which have been seen so far during this run's execution and thus are
|
|
109
114
|
/// usable during replay.
|
|
110
115
|
observed_internal_flags: InternalFlagsRef,
|
|
116
|
+
/// Set on each WFT started event, the most recent size of history in bytes
|
|
117
|
+
history_size_bytes: u64,
|
|
118
|
+
/// Set on each WFT started event
|
|
119
|
+
continue_as_new_suggested: bool,
|
|
111
120
|
|
|
112
121
|
all_machines: SlotMap<MachineKey, Machines>,
|
|
113
122
|
/// If a machine key is in this map, that machine was created internally by core, not as a
|
|
@@ -137,10 +146,6 @@ pub(crate) struct WorkflowMachines {
|
|
|
137
146
|
/// The workflow that is being driven by this instance of the machines
|
|
138
147
|
drive_me: DrivenWorkflow,
|
|
139
148
|
|
|
140
|
-
/// Is set to true once we've seen the final event in workflow history, to avoid accidentally
|
|
141
|
-
/// re-applying the final workflow task.
|
|
142
|
-
pub have_seen_terminal_event: bool,
|
|
143
|
-
|
|
144
149
|
/// Metrics context
|
|
145
150
|
pub metrics: MetricsContext,
|
|
146
151
|
}
|
|
@@ -229,6 +234,7 @@ impl WorkflowMachines {
|
|
|
229
234
|
workflow_id: basics.workflow_id,
|
|
230
235
|
workflow_type: basics.workflow_type,
|
|
231
236
|
run_id: basics.run_id,
|
|
237
|
+
task_queue: basics.task_queue,
|
|
232
238
|
drive_me: driven_wf,
|
|
233
239
|
replaying,
|
|
234
240
|
metrics: basics.metrics,
|
|
@@ -241,6 +247,8 @@ impl WorkflowMachines {
|
|
|
241
247
|
wft_start_time: None,
|
|
242
248
|
current_wf_time: None,
|
|
243
249
|
observed_internal_flags: Rc::new(RefCell::new(observed_internal_flags)),
|
|
250
|
+
history_size_bytes: 0,
|
|
251
|
+
continue_as_new_suggested: false,
|
|
244
252
|
all_machines: Default::default(),
|
|
245
253
|
machine_is_core_created: Default::default(),
|
|
246
254
|
machines_by_event_id: Default::default(),
|
|
@@ -369,6 +377,8 @@ impl WorkflowMachines {
|
|
|
369
377
|
.borrow()
|
|
370
378
|
.all_lang()
|
|
371
379
|
.collect(),
|
|
380
|
+
history_size_bytes: self.history_size_bytes,
|
|
381
|
+
continue_as_new_suggested: self.continue_as_new_suggested,
|
|
372
382
|
}
|
|
373
383
|
}
|
|
374
384
|
|
|
@@ -635,6 +645,13 @@ impl WorkflowMachines {
|
|
|
635
645
|
return self.handle_command_event(event_dat, next_event);
|
|
636
646
|
}
|
|
637
647
|
|
|
648
|
+
if let Some(history_event::Attributes::WorkflowTaskStartedEventAttributes(ref attrs)) =
|
|
649
|
+
event.attributes
|
|
650
|
+
{
|
|
651
|
+
self.history_size_bytes = u64::try_from(attrs.history_size_bytes).unwrap_or_default();
|
|
652
|
+
self.continue_as_new_suggested = attrs.suggest_continue_as_new;
|
|
653
|
+
}
|
|
654
|
+
|
|
638
655
|
if let Some(initial_cmd_id) = event.get_initial_command_event_id() {
|
|
639
656
|
// We remove the machine while we it handles events, then return it, to avoid
|
|
640
657
|
// borrowing from ourself mutably.
|
|
@@ -1050,24 +1067,27 @@ impl WorkflowMachines {
|
|
|
1050
1067
|
}
|
|
1051
1068
|
WFCommand::AddActivity(attrs) => {
|
|
1052
1069
|
let seq = attrs.seq;
|
|
1070
|
+
let use_compat = self.determine_use_compatible_flag(
|
|
1071
|
+
attrs.versioning_intent(),
|
|
1072
|
+
&attrs.task_queue,
|
|
1073
|
+
);
|
|
1053
1074
|
self.add_cmd_to_wf_task(
|
|
1054
|
-
ActivityMachine::new_scheduled(
|
|
1075
|
+
ActivityMachine::new_scheduled(
|
|
1076
|
+
attrs,
|
|
1077
|
+
self.observed_internal_flags.clone(),
|
|
1078
|
+
use_compat,
|
|
1079
|
+
),
|
|
1055
1080
|
CommandID::Activity(seq).into(),
|
|
1056
1081
|
);
|
|
1057
1082
|
}
|
|
1058
1083
|
WFCommand::AddLocalActivity(attrs) => {
|
|
1059
1084
|
let seq = attrs.seq;
|
|
1060
|
-
let attrs: ValidScheduleLA =
|
|
1061
|
-
attrs
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
.map_err(|e| {
|
|
1067
|
-
WFMachinesError::Fatal(format!(
|
|
1068
|
-
"Invalid schedule local activity request (seq {seq}): {e}"
|
|
1069
|
-
))
|
|
1070
|
-
})?;
|
|
1085
|
+
let attrs: ValidScheduleLA =
|
|
1086
|
+
ValidScheduleLA::from_schedule_la(attrs).map_err(|e| {
|
|
1087
|
+
WFMachinesError::Fatal(format!(
|
|
1088
|
+
"Invalid schedule local activity request (seq {seq}): {e}"
|
|
1089
|
+
))
|
|
1090
|
+
})?;
|
|
1071
1091
|
let (la, mach_resp) = new_local_activity(
|
|
1072
1092
|
attrs,
|
|
1073
1093
|
self.replaying,
|
|
@@ -1087,20 +1107,32 @@ impl WorkflowMachines {
|
|
|
1087
1107
|
self.process_cancellation(CommandID::LocalActivity(attrs.seq))?;
|
|
1088
1108
|
}
|
|
1089
1109
|
WFCommand::CompleteWorkflow(attrs) => {
|
|
1090
|
-
self.
|
|
1110
|
+
if !self.replaying {
|
|
1111
|
+
self.metrics.wf_completed();
|
|
1112
|
+
}
|
|
1091
1113
|
self.add_terminal_command(complete_workflow(attrs));
|
|
1092
1114
|
}
|
|
1093
1115
|
WFCommand::FailWorkflow(attrs) => {
|
|
1094
|
-
self.
|
|
1116
|
+
if !self.replaying {
|
|
1117
|
+
self.metrics.wf_failed();
|
|
1118
|
+
}
|
|
1095
1119
|
self.add_terminal_command(fail_workflow(attrs));
|
|
1096
1120
|
}
|
|
1097
1121
|
WFCommand::ContinueAsNew(attrs) => {
|
|
1098
|
-
self.
|
|
1122
|
+
if !self.replaying {
|
|
1123
|
+
self.metrics.wf_continued_as_new();
|
|
1124
|
+
}
|
|
1099
1125
|
let attrs = self.augment_continue_as_new_with_current_values(attrs);
|
|
1100
|
-
self.
|
|
1126
|
+
let use_compat = self.determine_use_compatible_flag(
|
|
1127
|
+
attrs.versioning_intent(),
|
|
1128
|
+
&attrs.task_queue,
|
|
1129
|
+
);
|
|
1130
|
+
self.add_terminal_command(continue_as_new(attrs, use_compat));
|
|
1101
1131
|
}
|
|
1102
1132
|
WFCommand::CancelWorkflow(attrs) => {
|
|
1103
|
-
self.
|
|
1133
|
+
if !self.replaying {
|
|
1134
|
+
self.metrics.wf_canceled();
|
|
1135
|
+
}
|
|
1104
1136
|
self.add_terminal_command(cancel_workflow(attrs));
|
|
1105
1137
|
}
|
|
1106
1138
|
WFCommand::SetPatchMarker(attrs) => {
|
|
@@ -1136,10 +1168,15 @@ impl WorkflowMachines {
|
|
|
1136
1168
|
}
|
|
1137
1169
|
WFCommand::AddChildWorkflow(attrs) => {
|
|
1138
1170
|
let seq = attrs.seq;
|
|
1171
|
+
let use_compat = self.determine_use_compatible_flag(
|
|
1172
|
+
attrs.versioning_intent(),
|
|
1173
|
+
&attrs.task_queue,
|
|
1174
|
+
);
|
|
1139
1175
|
self.add_cmd_to_wf_task(
|
|
1140
1176
|
ChildWorkflowMachine::new_scheduled(
|
|
1141
1177
|
attrs,
|
|
1142
1178
|
self.observed_internal_flags.clone(),
|
|
1179
|
+
use_compat,
|
|
1143
1180
|
),
|
|
1144
1181
|
CommandID::ChildWorkflowStart(seq).into(),
|
|
1145
1182
|
);
|
|
@@ -1289,6 +1326,21 @@ impl WorkflowMachines {
|
|
|
1289
1326
|
}
|
|
1290
1327
|
attrs
|
|
1291
1328
|
}
|
|
1329
|
+
|
|
1330
|
+
/// Given a user's versioning intent for a command and that command's target task queue,
|
|
1331
|
+
/// returns whether or not the command should set the flag for attempting to stick within the
|
|
1332
|
+
/// compatible version set
|
|
1333
|
+
fn determine_use_compatible_flag(&self, intent: VersioningIntent, target_tq: &str) -> bool {
|
|
1334
|
+
match intent {
|
|
1335
|
+
VersioningIntent::Compatible => true,
|
|
1336
|
+
VersioningIntent::Default => false,
|
|
1337
|
+
VersioningIntent::Unspecified => {
|
|
1338
|
+
// If the target TQ is empty, that means use same TQ.
|
|
1339
|
+
// When TQs match, use compat by default
|
|
1340
|
+
target_tq.is_empty() || target_tq == self.task_queue
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1292
1344
|
}
|
|
1293
1345
|
|
|
1294
1346
|
fn str_to_randomness_seed(run_id: &str) -> u64 {
|
|
@@ -61,7 +61,7 @@ pub struct ManagedWFFunc {
|
|
|
61
61
|
activation_tx: UnboundedSender<WorkflowActivation>,
|
|
62
62
|
completions_rx: UnboundedReceiver<WorkflowActivationCompletion>,
|
|
63
63
|
completions_sync_tx: crossbeam::channel::Sender<WorkflowActivationCompletion>,
|
|
64
|
-
future_handle: Option<JoinHandle<WorkflowResult<
|
|
64
|
+
future_handle: Option<JoinHandle<WorkflowResult<Payload>>>,
|
|
65
65
|
was_shutdown: bool,
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -93,6 +93,7 @@ impl ManagedWFFunc {
|
|
|
93
93
|
workflow_id: "wfid".to_string(),
|
|
94
94
|
workflow_type: "wftype".to_string(),
|
|
95
95
|
run_id: "runid".to_string(),
|
|
96
|
+
task_queue: TEST_Q.to_string(),
|
|
96
97
|
history: hist,
|
|
97
98
|
metrics: MetricsContext::no_op(),
|
|
98
99
|
capabilities: DEFAULT_TEST_CAPABILITIES,
|
|
@@ -175,7 +176,7 @@ impl ManagedWFFunc {
|
|
|
175
176
|
Ok(last_act)
|
|
176
177
|
}
|
|
177
178
|
|
|
178
|
-
pub async fn shutdown(&mut self) -> WorkflowResult<
|
|
179
|
+
pub async fn shutdown(&mut self) -> WorkflowResult<Payload> {
|
|
179
180
|
self.was_shutdown = true;
|
|
180
181
|
// Send an eviction to ensure wf exits if it has not finished (ex: feeding partial hist)
|
|
181
182
|
let _ = self.activation_tx.send(create_evict_activation(
|
|
@@ -1039,13 +1039,26 @@ impl ManagedRun {
|
|
|
1039
1039
|
}
|
|
1040
1040
|
|
|
1041
1041
|
fn reply_to_complete(
|
|
1042
|
-
&self,
|
|
1042
|
+
&mut self,
|
|
1043
1043
|
outcome: ActivationCompleteOutcome,
|
|
1044
1044
|
chan: Option<oneshot::Sender<ActivationCompleteResult>>,
|
|
1045
1045
|
) {
|
|
1046
1046
|
if let Some(chan) = chan {
|
|
1047
|
-
chan
|
|
1048
|
-
.
|
|
1047
|
+
if chan
|
|
1048
|
+
.send(self.build_activation_complete_result(outcome))
|
|
1049
|
+
.is_err()
|
|
1050
|
+
{
|
|
1051
|
+
let warnstr = "The workflow task completer went missing! This likely indicates an \
|
|
1052
|
+
SDK bug, please report."
|
|
1053
|
+
.to_string();
|
|
1054
|
+
warn!(run_id=%self.run_id(), "{}", warnstr);
|
|
1055
|
+
self.request_eviction(RequestEvictMsg {
|
|
1056
|
+
run_id: self.run_id().to_string(),
|
|
1057
|
+
message: warnstr,
|
|
1058
|
+
reason: EvictionReason::Fatal,
|
|
1059
|
+
auto_reply_fail_tt: None,
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1049
1062
|
}
|
|
1050
1063
|
}
|
|
1051
1064
|
|
|
@@ -23,22 +23,19 @@ pub(crate) use managed_run::ManagedWFFunc;
|
|
|
23
23
|
|
|
24
24
|
use crate::{
|
|
25
25
|
abstractions::{
|
|
26
|
-
dbg_panic,
|
|
27
|
-
|
|
26
|
+
dbg_panic, take_cell::TakeCell, MeteredSemaphore, TrackedOwnedMeteredSemPermit,
|
|
27
|
+
UsedMeteredSemPermit,
|
|
28
28
|
},
|
|
29
29
|
internal_flags::InternalFlags,
|
|
30
|
-
protosext::
|
|
31
|
-
telemetry::{
|
|
32
|
-
metrics::workflow_worker_type, set_trace_subscriber_for_current_thread, TelemetryInstance,
|
|
33
|
-
VecDisplayer,
|
|
34
|
-
},
|
|
30
|
+
protosext::legacy_query_failure,
|
|
31
|
+
telemetry::{set_trace_subscriber_for_current_thread, TelemetryInstance, VecDisplayer},
|
|
35
32
|
worker::{
|
|
36
33
|
activities::{ActivitiesFromWFTsHandle, LocalActivityManager, TrackedPermittedTqResp},
|
|
37
34
|
client::{WorkerClient, WorkflowTaskCompletion},
|
|
38
35
|
workflow::{
|
|
39
36
|
history_update::HistoryPaginator,
|
|
40
37
|
managed_run::RunUpdateAct,
|
|
41
|
-
wft_extraction::{HistoryFetchReq, WFTExtractor},
|
|
38
|
+
wft_extraction::{HistoryFetchReq, WFTExtractor, WFTStreamIn},
|
|
42
39
|
wft_poller::validate_wft,
|
|
43
40
|
workflow_stream::{LocalInput, LocalInputs, WFStream},
|
|
44
41
|
},
|
|
@@ -127,14 +124,13 @@ pub(crate) struct Workflows {
|
|
|
127
124
|
/// If set, can be used to reserve activity task slots for eager-return of new activity tasks.
|
|
128
125
|
activity_tasks_handle: Option<ActivitiesFromWFTsHandle>,
|
|
129
126
|
/// Ensures we stay at or below this worker's maximum concurrent workflow task limit
|
|
130
|
-
wft_semaphore: MeteredSemaphore
|
|
127
|
+
wft_semaphore: Arc<MeteredSemaphore>,
|
|
131
128
|
local_act_mgr: Arc<LocalActivityManager>,
|
|
132
129
|
ever_polled: AtomicBool,
|
|
133
130
|
}
|
|
134
131
|
|
|
135
132
|
pub(crate) struct WorkflowBasics {
|
|
136
133
|
pub max_cached_workflows: usize,
|
|
137
|
-
pub max_outstanding_wfts: usize,
|
|
138
134
|
pub shutdown_token: CancellationToken,
|
|
139
135
|
pub metrics: MetricsContext,
|
|
140
136
|
pub namespace: String,
|
|
@@ -151,6 +147,7 @@ pub(crate) struct RunBasics<'a> {
|
|
|
151
147
|
pub workflow_id: String,
|
|
152
148
|
pub workflow_type: String,
|
|
153
149
|
pub run_id: String,
|
|
150
|
+
pub task_queue: String,
|
|
154
151
|
pub history: HistoryUpdate,
|
|
155
152
|
pub metrics: MetricsContext,
|
|
156
153
|
pub capabilities: &'a get_system_info_response::Capabilities,
|
|
@@ -162,7 +159,8 @@ impl Workflows {
|
|
|
162
159
|
basics: WorkflowBasics,
|
|
163
160
|
sticky_attrs: Option<StickyExecutionAttributes>,
|
|
164
161
|
client: Arc<dyn WorkerClient>,
|
|
165
|
-
|
|
162
|
+
wft_semaphore: Arc<MeteredSemaphore>,
|
|
163
|
+
wft_stream: impl Stream<Item = WFTStreamIn> + Send + 'static,
|
|
166
164
|
local_activity_request_sink: impl LocalActivityRequestSink,
|
|
167
165
|
local_act_mgr: Arc<LocalActivityManager>,
|
|
168
166
|
heartbeat_timeout_rx: UnboundedReceiver<HeartbeatTimeoutMsg>,
|
|
@@ -173,16 +171,6 @@ impl Workflows {
|
|
|
173
171
|
let (fetch_tx, fetch_rx) = unbounded_channel();
|
|
174
172
|
let shutdown_tok = basics.shutdown_token.clone();
|
|
175
173
|
let task_queue = basics.task_queue.clone();
|
|
176
|
-
let wft_semaphore = MeteredSemaphore::new(
|
|
177
|
-
basics.max_outstanding_wfts,
|
|
178
|
-
basics.metrics.with_new_attrs([workflow_worker_type()]),
|
|
179
|
-
MetricsContext::available_task_slots,
|
|
180
|
-
);
|
|
181
|
-
// Only allow polling of the new WFT stream if there are available task slots
|
|
182
|
-
let proceeder = stream::unfold(wft_semaphore.clone(), |sem| async move {
|
|
183
|
-
Some((sem.acquire_owned().await.unwrap(), sem))
|
|
184
|
-
});
|
|
185
|
-
let wft_stream = stream_when_allowed(wft_stream, proceeder);
|
|
186
174
|
let extracted_wft_stream = WFTExtractor::build(
|
|
187
175
|
client.clone(),
|
|
188
176
|
basics.fetching_concurrency,
|
|
@@ -515,6 +503,10 @@ impl Workflows {
|
|
|
515
503
|
pub(super) fn available_wft_permits(&self) -> usize {
|
|
516
504
|
self.wft_semaphore.available_permits()
|
|
517
505
|
}
|
|
506
|
+
#[cfg(test)]
|
|
507
|
+
pub(super) fn unused_wft_permits(&self) -> usize {
|
|
508
|
+
self.wft_semaphore.unused_permits()
|
|
509
|
+
}
|
|
518
510
|
|
|
519
511
|
pub(super) async fn shutdown(&self) -> Result<(), anyhow::Error> {
|
|
520
512
|
if let Some(jh) = self.processing_task.take_once() {
|
|
@@ -1235,7 +1227,6 @@ enum CommandID {
|
|
|
1235
1227
|
#[derive(Debug, Clone)]
|
|
1236
1228
|
pub struct WorkflowStartedInfo {
|
|
1237
1229
|
workflow_task_timeout: Option<Duration>,
|
|
1238
|
-
workflow_execution_timeout: Option<Duration>,
|
|
1239
1230
|
memo: Option<Memo>,
|
|
1240
1231
|
search_attrs: Option<SearchAttributes>,
|
|
1241
1232
|
retry_policy: Option<RetryPolicy>,
|
|
@@ -13,6 +13,7 @@ use temporal_sdk_core_protos::temporal::api::workflowservice::v1::get_system_inf
|
|
|
13
13
|
pub(super) struct RunCache {
|
|
14
14
|
max: usize,
|
|
15
15
|
namespace: String,
|
|
16
|
+
task_queue: String,
|
|
16
17
|
server_capabilities: get_system_info_response::Capabilities,
|
|
17
18
|
/// Run id -> Data
|
|
18
19
|
runs: LruCache<String, ManagedRun>,
|
|
@@ -25,6 +26,7 @@ impl RunCache {
|
|
|
25
26
|
pub fn new(
|
|
26
27
|
max_cache_size: usize,
|
|
27
28
|
namespace: String,
|
|
29
|
+
task_queue: String,
|
|
28
30
|
server_capabilities: get_system_info_response::Capabilities,
|
|
29
31
|
local_activity_request_sink: impl LocalActivityRequestSink,
|
|
30
32
|
metrics: MetricsContext,
|
|
@@ -39,6 +41,7 @@ impl RunCache {
|
|
|
39
41
|
Self {
|
|
40
42
|
max: max_cache_size,
|
|
41
43
|
namespace,
|
|
44
|
+
task_queue,
|
|
42
45
|
server_capabilities,
|
|
43
46
|
runs: LruCache::new(
|
|
44
47
|
NonZeroUsize::new(lru_size).expect("LRU size is guaranteed positive"),
|
|
@@ -72,6 +75,7 @@ impl RunCache {
|
|
|
72
75
|
workflow_id: pwft.work.execution.workflow_id.clone(),
|
|
73
76
|
workflow_type: pwft.work.workflow_type.clone(),
|
|
74
77
|
run_id: pwft.work.execution.run_id.clone(),
|
|
78
|
+
task_queue: self.task_queue.clone(),
|
|
75
79
|
history: history_update,
|
|
76
80
|
metrics,
|
|
77
81
|
capabilities: &self.server_capabilities,
|
|
@@ -89,6 +93,7 @@ impl RunCache {
|
|
|
89
93
|
pub fn remove(&mut self, k: &str) -> Option<ManagedRun> {
|
|
90
94
|
let r = self.runs.pop(k);
|
|
91
95
|
self.metrics.cache_size(self.len() as u64);
|
|
96
|
+
self.metrics.cache_eviction();
|
|
92
97
|
r
|
|
93
98
|
}
|
|
94
99
|
|
|
@@ -36,10 +36,7 @@ pub(super) enum WFTExtractorOutput {
|
|
|
36
36
|
PollerDead,
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
type WFTStreamIn = (
|
|
40
|
-
Result<ValidPollWFTQResponse, tonic::Status>,
|
|
41
|
-
OwnedMeteredSemPermit,
|
|
42
|
-
);
|
|
39
|
+
pub(crate) type WFTStreamIn = Result<(ValidPollWFTQResponse, OwnedMeteredSemPermit), tonic::Status>;
|
|
43
40
|
#[derive(derive_more::From, Debug)]
|
|
44
41
|
pub(super) enum HistoryFetchReq {
|
|
45
42
|
Full(CacheMissFetchReq, Arc<HistfetchRC>),
|
|
@@ -58,11 +55,11 @@ impl WFTExtractor {
|
|
|
58
55
|
) -> impl Stream<Item = Result<WFTExtractorOutput, tonic::Status>> + Send + 'static {
|
|
59
56
|
let fetch_client = client.clone();
|
|
60
57
|
let wft_stream = wft_stream
|
|
61
|
-
.map(move |
|
|
58
|
+
.map(move |stream_in| {
|
|
62
59
|
let client = client.clone();
|
|
63
60
|
async move {
|
|
64
|
-
match
|
|
65
|
-
Ok(wft) => {
|
|
61
|
+
match stream_in {
|
|
62
|
+
Ok((wft, permit)) => {
|
|
66
63
|
let run_id = wft.workflow_execution.run_id.clone();
|
|
67
64
|
let tt = wft.task_token.clone();
|
|
68
65
|
Ok(match HistoryPaginator::from_poll(wft, client).await {
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
use crate::{
|
|
1
|
+
use crate::{
|
|
2
|
+
abstractions::OwnedMeteredSemPermit,
|
|
3
|
+
pollers::{BoxedWFPoller, Poller},
|
|
4
|
+
protosext::ValidPollWFTQResponse,
|
|
5
|
+
MetricsContext,
|
|
6
|
+
};
|
|
2
7
|
use futures::{stream, Stream};
|
|
3
8
|
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollWorkflowTaskQueueResponse;
|
|
4
9
|
|
|
5
10
|
pub(crate) fn new_wft_poller(
|
|
6
11
|
poller: BoxedWFPoller,
|
|
7
12
|
metrics: MetricsContext,
|
|
8
|
-
) -> impl Stream<Item = Result<ValidPollWFTQResponse, tonic::Status>> {
|
|
13
|
+
) -> impl Stream<Item = Result<(ValidPollWFTQResponse, OwnedMeteredSemPermit), tonic::Status>> {
|
|
9
14
|
stream::unfold((poller, metrics), |(poller, metrics)| async move {
|
|
10
15
|
loop {
|
|
11
16
|
return match poller.poll().await {
|
|
12
|
-
Some(Ok(wft)) => {
|
|
17
|
+
Some(Ok((wft, permit))) => {
|
|
13
18
|
if wft == PollWorkflowTaskQueueResponse::default() {
|
|
14
19
|
// We get the default proto in the event that the long poll times out.
|
|
15
20
|
debug!("Poll wft timeout");
|
|
@@ -27,7 +32,7 @@ pub(crate) fn new_wft_poller(
|
|
|
27
32
|
}
|
|
28
33
|
};
|
|
29
34
|
metrics.wf_tq_poll_ok();
|
|
30
|
-
Some((Ok(work), (poller, metrics)))
|
|
35
|
+
Some((Ok((work, permit)), (poller, metrics)))
|
|
31
36
|
}
|
|
32
37
|
Some(Err(e)) => {
|
|
33
38
|
warn!(error=?e, "Error while polling for workflow tasks");
|
|
@@ -35,7 +40,12 @@ pub(crate) fn new_wft_poller(
|
|
|
35
40
|
}
|
|
36
41
|
// If poller returns None, it's dead, thus we also return None to terminate this
|
|
37
42
|
// stream.
|
|
38
|
-
None =>
|
|
43
|
+
None => {
|
|
44
|
+
// Make sure we call the actual shutdown function here to propagate any panics
|
|
45
|
+
// inside the polling tasks as errors.
|
|
46
|
+
poller.shutdown_box().await;
|
|
47
|
+
None
|
|
48
|
+
}
|
|
39
49
|
};
|
|
40
50
|
}
|
|
41
51
|
})
|
|
@@ -55,8 +65,11 @@ pub(crate) fn validate_wft(
|
|
|
55
65
|
#[cfg(test)]
|
|
56
66
|
mod tests {
|
|
57
67
|
use super::*;
|
|
58
|
-
use crate::
|
|
68
|
+
use crate::{
|
|
69
|
+
abstractions::MeteredSemaphore, pollers::MockPermittedPollBuffer, test_help::mock_poller,
|
|
70
|
+
};
|
|
59
71
|
use futures::{pin_mut, StreamExt};
|
|
72
|
+
use std::sync::Arc;
|
|
60
73
|
|
|
61
74
|
#[tokio::test]
|
|
62
75
|
async fn poll_timeouts_do_not_produce_responses() {
|
|
@@ -66,7 +79,16 @@ mod tests {
|
|
|
66
79
|
.times(1)
|
|
67
80
|
.returning(|| Some(Ok(PollWorkflowTaskQueueResponse::default())));
|
|
68
81
|
mock_poller.expect_poll().times(1).returning(|| None);
|
|
69
|
-
|
|
82
|
+
mock_poller.expect_shutdown().times(1).returning(|| ());
|
|
83
|
+
let sem = Arc::new(MeteredSemaphore::new(
|
|
84
|
+
10,
|
|
85
|
+
MetricsContext::no_op(),
|
|
86
|
+
MetricsContext::available_task_slots,
|
|
87
|
+
));
|
|
88
|
+
let stream = new_wft_poller(
|
|
89
|
+
Box::new(MockPermittedPollBuffer::new(sem, mock_poller)),
|
|
90
|
+
MetricsContext::no_op(),
|
|
91
|
+
);
|
|
70
92
|
pin_mut!(stream);
|
|
71
93
|
assert_matches!(stream.next().await, None);
|
|
72
94
|
}
|
|
@@ -78,7 +100,15 @@ mod tests {
|
|
|
78
100
|
.expect_poll()
|
|
79
101
|
.times(1)
|
|
80
102
|
.returning(|| Some(Err(tonic::Status::internal("ahhh"))));
|
|
81
|
-
let
|
|
103
|
+
let sem = Arc::new(MeteredSemaphore::new(
|
|
104
|
+
10,
|
|
105
|
+
MetricsContext::no_op(),
|
|
106
|
+
MetricsContext::available_task_slots,
|
|
107
|
+
));
|
|
108
|
+
let stream = new_wft_poller(
|
|
109
|
+
Box::new(MockPermittedPollBuffer::new(sem, mock_poller)),
|
|
110
|
+
MetricsContext::no_op(),
|
|
111
|
+
);
|
|
82
112
|
pin_mut!(stream);
|
|
83
113
|
assert_matches!(stream.next().await, Some(Err(_)));
|
|
84
114
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
use std::time::Duration;
|
|
2
2
|
use tokio::sync::mpsc::UnboundedSender;
|
|
3
3
|
|
|
4
|
+
const MAX_OUTSTANDING_WFT_DEFAULT: usize = 100;
|
|
5
|
+
const MAX_CONCURRENT_WFT_POLLS_DEFAULT: usize = 5;
|
|
6
|
+
|
|
4
7
|
/// Defines per-worker configuration options
|
|
5
8
|
#[derive(Debug, Clone, derive_builder::Builder, serde::Serialize, serde::Deserialize)]
|
|
6
9
|
#[builder(setter(into), build_fn(validate = "Self::validate"))]
|
|
@@ -31,7 +34,7 @@ pub struct WorkerConfig {
|
|
|
31
34
|
/// "outstanding" until all activations it requires have been completed.
|
|
32
35
|
///
|
|
33
36
|
/// Cannot be larger than `max_cached_workflows`.
|
|
34
|
-
#[builder(default = "
|
|
37
|
+
#[builder(default = "MAX_OUTSTANDING_WFT_DEFAULT")]
|
|
35
38
|
pub max_outstanding_workflow_tasks: usize,
|
|
36
39
|
/// The maximum number of activity tasks that will ever be given to this worker concurrently
|
|
37
40
|
#[builder(default = "100")]
|
|
@@ -43,7 +46,7 @@ pub struct WorkerConfig {
|
|
|
43
46
|
/// Maximum number of concurrent poll workflow task requests we will perform at a time on this
|
|
44
47
|
/// worker's task queue. See also [WorkerConfig::nonsticky_to_sticky_poll_ratio]. Must be at
|
|
45
48
|
/// least 1.
|
|
46
|
-
#[builder(default = "
|
|
49
|
+
#[builder(default = "MAX_CONCURRENT_WFT_POLLS_DEFAULT")]
|
|
47
50
|
pub max_concurrent_wft_polls: usize,
|
|
48
51
|
/// [WorkerConfig::max_concurrent_wft_polls] * this number = the number of max pollers that will
|
|
49
52
|
/// be allowed for the nonsticky queue when sticky tasks are enabled. If both defaults are used,
|
|
@@ -161,6 +164,44 @@ impl WorkerConfigBuilder {
|
|
|
161
164
|
);
|
|
162
165
|
}
|
|
163
166
|
}
|
|
167
|
+
if matches!(self.max_concurrent_wft_polls, Some(1))
|
|
168
|
+
&& self.max_cached_workflows > Some(0)
|
|
169
|
+
&& self
|
|
170
|
+
.max_outstanding_workflow_tasks
|
|
171
|
+
.unwrap_or(MAX_OUTSTANDING_WFT_DEFAULT)
|
|
172
|
+
<= 1
|
|
173
|
+
{
|
|
174
|
+
return Err(
|
|
175
|
+
"`max_outstanding_workflow_tasks` must be at at least 2 when \
|
|
176
|
+
`max_cached_workflows` is nonzero"
|
|
177
|
+
.to_owned(),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
if self
|
|
181
|
+
.max_concurrent_wft_polls
|
|
182
|
+
.unwrap_or(MAX_CONCURRENT_WFT_POLLS_DEFAULT)
|
|
183
|
+
> self
|
|
184
|
+
.max_outstanding_workflow_tasks
|
|
185
|
+
.unwrap_or(MAX_OUTSTANDING_WFT_DEFAULT)
|
|
186
|
+
{
|
|
187
|
+
return Err(
|
|
188
|
+
"`max_concurrent_wft_polls` cannot exceed `max_outstanding_workflow_tasks`"
|
|
189
|
+
.to_owned(),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if self.use_worker_versioning.unwrap_or_default()
|
|
194
|
+
&& self
|
|
195
|
+
.worker_build_id
|
|
196
|
+
.as_ref()
|
|
197
|
+
.map(|s| s.is_empty())
|
|
198
|
+
.unwrap_or_default()
|
|
199
|
+
{
|
|
200
|
+
return Err(
|
|
201
|
+
"`worker_build_id` must be non-empty when `use_worker_versioning` is true"
|
|
202
|
+
.to_owned(),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
164
205
|
Ok(())
|
|
165
206
|
}
|
|
166
207
|
}
|
|
@@ -29,7 +29,7 @@ $(PROTO_OUT):
|
|
|
29
29
|
mkdir $(PROTO_OUT)
|
|
30
30
|
|
|
31
31
|
##### Compile proto files for go #####
|
|
32
|
-
grpc: buf-lint api-linter gogo-grpc fix-path
|
|
32
|
+
grpc: buf-lint api-linter buf-breaking gogo-grpc fix-path
|
|
33
33
|
|
|
34
34
|
go-grpc: clean $(PROTO_OUT)
|
|
35
35
|
printf $(COLOR) "Compile for go-gRPC..."
|
|
@@ -37,6 +37,7 @@ import "google/protobuf/timestamp.proto";
|
|
|
37
37
|
|
|
38
38
|
import "temporal/api/common/v1/message.proto";
|
|
39
39
|
import "temporal/api/enums/v1/batch_operation.proto";
|
|
40
|
+
import "temporal/api/enums/v1/reset.proto";
|
|
40
41
|
|
|
41
42
|
message BatchOperationInfo {
|
|
42
43
|
// Batch job ID
|
|
@@ -86,4 +87,15 @@ message BatchOperationCancellation {
|
|
|
86
87
|
message BatchOperationDeletion {
|
|
87
88
|
// The identity of the worker/client
|
|
88
89
|
string identity = 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// BatchOperationReset sends reset requests to batch workflows.
|
|
93
|
+
// Keep the parameter in sync with temporal.api.workflowservice.v1.ResetWorkflowExecutionRequest.
|
|
94
|
+
message BatchOperationReset {
|
|
95
|
+
// Reset type.
|
|
96
|
+
temporal.api.enums.v1.ResetType reset_type = 1;
|
|
97
|
+
// History event reapply options.
|
|
98
|
+
temporal.api.enums.v1.ResetReapplyType reset_reapply_type = 2;
|
|
99
|
+
// The identity of the worker/client.
|
|
100
|
+
string identity = 3;
|
|
89
101
|
}
|
|
@@ -83,6 +83,10 @@ message ScheduleActivityTaskCommandAttributes {
|
|
|
83
83
|
// Request to start the activity directly bypassing matching service and worker polling
|
|
84
84
|
// The slot for executing the activity should be reserved when setting this field to true.
|
|
85
85
|
bool request_eager_execution = 12;
|
|
86
|
+
// If this is set, the workflow executing this command wishes to start the activity using
|
|
87
|
+
// a version compatible with the version that this workflow most recently ran on, if such
|
|
88
|
+
// behavior is possible.
|
|
89
|
+
bool use_compatible_version = 13;
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
message RequestCancelActivityTaskCommandAttributes {
|
|
@@ -191,6 +195,9 @@ message ContinueAsNewWorkflowExecutionCommandAttributes {
|
|
|
191
195
|
temporal.api.common.v1.Header header = 12;
|
|
192
196
|
temporal.api.common.v1.Memo memo = 13;
|
|
193
197
|
temporal.api.common.v1.SearchAttributes search_attributes = 14;
|
|
198
|
+
// If this is set, the workflow executing this command wishes to continue as new using a version
|
|
199
|
+
// compatible with the version that this workflow most recently ran on.
|
|
200
|
+
bool use_compatible_version = 15;
|
|
194
201
|
|
|
195
202
|
// `workflow_execution_timeout` is omitted as it shouldn't be overridden from within a workflow.
|
|
196
203
|
}
|
|
@@ -218,6 +225,10 @@ message StartChildWorkflowExecutionCommandAttributes {
|
|
|
218
225
|
temporal.api.common.v1.Header header = 14;
|
|
219
226
|
temporal.api.common.v1.Memo memo = 15;
|
|
220
227
|
temporal.api.common.v1.SearchAttributes search_attributes = 16;
|
|
228
|
+
// If this is set, the workflow executing this command wishes to start the child workflow using
|
|
229
|
+
// a version compatible with the version that this workflow most recently ran on, if such
|
|
230
|
+
// behavior is possible.
|
|
231
|
+
bool use_compatible_version = 17;
|
|
221
232
|
}
|
|
222
233
|
|
|
223
234
|
message ProtocolMessageCommandAttributes {
|