@temporalio/core-bridge 1.7.0 → 1.7.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 +479 -400
- 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/client/src/lib.rs +23 -6
- package/sdk-core/core/src/core_tests/determinism.rs +49 -2
- package/sdk-core/core/src/internal_flags.rs +0 -14
- package/sdk-core/core/src/worker/mod.rs +1 -0
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +23 -88
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +1 -4
- package/sdk-core/core/src/worker/workflow/managed_run.rs +10 -2
- package/sdk-core/core/src/worker/workflow/mod.rs +22 -8
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +29 -27
- package/sdk-core/tests/main.rs +15 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@temporalio/core-bridge",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "Temporal.io SDK Core<>Node bridge",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@opentelemetry/api": "^1.3.0",
|
|
26
|
-
"@temporalio/common": "1.7.
|
|
26
|
+
"@temporalio/common": "1.7.1",
|
|
27
27
|
"arg": "^5.0.2",
|
|
28
28
|
"cargo-cp-artifact": "^0.1.6",
|
|
29
29
|
"which": "^2.0.2"
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
"publishConfig": {
|
|
54
54
|
"access": "public"
|
|
55
55
|
},
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "368aa83c631555fc31cff25f4af8817d069878d8"
|
|
57
57
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -33,7 +33,7 @@ use crate::{
|
|
|
33
33
|
workflow_handle::UntypedWorkflowHandle,
|
|
34
34
|
};
|
|
35
35
|
use backoff::{exponential, ExponentialBackoff, SystemClock};
|
|
36
|
-
use http::uri::InvalidUri;
|
|
36
|
+
use http::{uri::InvalidUri, Uri};
|
|
37
37
|
use once_cell::sync::OnceCell;
|
|
38
38
|
use parking_lot::RwLock;
|
|
39
39
|
use std::{
|
|
@@ -114,6 +114,14 @@ pub struct ClientOptions {
|
|
|
114
114
|
/// Retry configuration for the server client. Default is [RetryConfig::default]
|
|
115
115
|
#[builder(default)]
|
|
116
116
|
pub retry_config: RetryConfig,
|
|
117
|
+
|
|
118
|
+
/// If set, override the origin used when connecting. May be useful in rare situations where tls
|
|
119
|
+
/// verification needs to use a different name from what should be set as the `:authority`
|
|
120
|
+
/// header. If [TlsConfig::domain] is set, and this is not, this will be set to
|
|
121
|
+
/// `https://<domain>`, effectively making the `:authority` header consistent with the domain
|
|
122
|
+
/// override.
|
|
123
|
+
#[builder(default)]
|
|
124
|
+
pub override_origin: Option<Uri>,
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
/// Configuration options for TLS
|
|
@@ -310,6 +318,11 @@ impl ClientOptions {
|
|
|
310
318
|
{
|
|
311
319
|
let channel = Channel::from_shared(self.target_url.to_string())?;
|
|
312
320
|
let channel = self.add_tls_to_channel(channel).await?;
|
|
321
|
+
let channel = if let Some(origin) = self.override_origin.clone() {
|
|
322
|
+
channel.origin(origin)
|
|
323
|
+
} else {
|
|
324
|
+
channel
|
|
325
|
+
};
|
|
313
326
|
let channel = channel.connect().await?;
|
|
314
327
|
let service = ServiceBuilder::new()
|
|
315
328
|
.layer_fn(|channel| GrpcMetricSvc {
|
|
@@ -347,10 +360,7 @@ impl ClientOptions {
|
|
|
347
360
|
|
|
348
361
|
/// If TLS is configured, set the appropriate options on the provided channel and return it.
|
|
349
362
|
/// Passes it through if TLS options not set.
|
|
350
|
-
async fn add_tls_to_channel(
|
|
351
|
-
&self,
|
|
352
|
-
channel: Endpoint,
|
|
353
|
-
) -> Result<Endpoint, tonic::transport::Error> {
|
|
363
|
+
async fn add_tls_to_channel(&self, mut channel: Endpoint) -> Result<Endpoint, ClientInitError> {
|
|
354
364
|
if let Some(tls_cfg) = &self.tls_cfg {
|
|
355
365
|
let mut tls = tonic::transport::ClientTlsConfig::new();
|
|
356
366
|
|
|
@@ -361,6 +371,13 @@ impl ClientOptions {
|
|
|
361
371
|
|
|
362
372
|
if let Some(domain) = &tls_cfg.domain {
|
|
363
373
|
tls = tls.domain_name(domain);
|
|
374
|
+
|
|
375
|
+
// This song and dance ultimately is just to make sure the `:authority` header ends
|
|
376
|
+
// up correct on requests while we use TLS. Setting the header directly in our
|
|
377
|
+
// interceptor doesn't work since seemingly it is overridden at some point by
|
|
378
|
+
// something lower level.
|
|
379
|
+
let uri: Uri = format!("https://{}", domain).parse()?;
|
|
380
|
+
channel = channel.origin(uri);
|
|
364
381
|
}
|
|
365
382
|
|
|
366
383
|
if let Some(client_opts) = &tls_cfg.client_tls_config {
|
|
@@ -369,7 +386,7 @@ impl ClientOptions {
|
|
|
369
386
|
tls = tls.identity(client_identity);
|
|
370
387
|
}
|
|
371
388
|
|
|
372
|
-
return channel.tls_config(tls);
|
|
389
|
+
return channel.tls_config(tls).map_err(Into::into);
|
|
373
390
|
}
|
|
374
391
|
Ok(channel)
|
|
375
392
|
}
|
|
@@ -13,8 +13,11 @@ use temporal_sdk::{
|
|
|
13
13
|
ActivityOptions, ChildWorkflowOptions, LocalActivityOptions, WfContext, WorkflowResult,
|
|
14
14
|
};
|
|
15
15
|
use temporal_sdk_core_protos::{
|
|
16
|
-
temporal::api::{
|
|
17
|
-
|
|
16
|
+
temporal::api::{
|
|
17
|
+
enums::v1::{EventType, WorkflowTaskFailedCause},
|
|
18
|
+
failure::v1::Failure,
|
|
19
|
+
},
|
|
20
|
+
TestHistoryBuilder, DEFAULT_ACTIVITY_TYPE,
|
|
18
21
|
};
|
|
19
22
|
|
|
20
23
|
static DID_FAIL: AtomicBool = AtomicBool::new(false);
|
|
@@ -268,3 +271,47 @@ async fn child_wf_id_or_type_change_is_nondeterministic(
|
|
|
268
271
|
.unwrap();
|
|
269
272
|
worker.run_until_done().await.unwrap();
|
|
270
273
|
}
|
|
274
|
+
|
|
275
|
+
/// Repros a situation where if, upon completing a task there is some internal error which causes
|
|
276
|
+
/// us to want to auto-fail the workflow task while there is also an outstanding eviction, the wf
|
|
277
|
+
/// would get evicted but then try to send some info down the completion channel afterward, causing
|
|
278
|
+
/// a panic.
|
|
279
|
+
#[tokio::test]
|
|
280
|
+
async fn repro_channel_missing_because_nondeterminism() {
|
|
281
|
+
for _ in 1..50 {
|
|
282
|
+
let wf_id = "fakeid";
|
|
283
|
+
let wf_type = DEFAULT_WORKFLOW_TYPE;
|
|
284
|
+
let mut t = TestHistoryBuilder::default();
|
|
285
|
+
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
286
|
+
t.add_full_wf_task();
|
|
287
|
+
t.add_has_change_marker("patch-1", false);
|
|
288
|
+
let _ts = t.add_by_type(EventType::TimerStarted);
|
|
289
|
+
t.add_workflow_task_scheduled_and_started();
|
|
290
|
+
|
|
291
|
+
let mock = mock_workflow_client();
|
|
292
|
+
let mut mh =
|
|
293
|
+
MockPollCfg::from_resp_batches(wf_id, t, [1.into(), ResponseType::AllHistory], mock);
|
|
294
|
+
mh.num_expected_fails = 1;
|
|
295
|
+
let mut worker = mock_sdk_cfg(mh, |cfg| {
|
|
296
|
+
cfg.max_cached_workflows = 2;
|
|
297
|
+
cfg.ignore_evicts_on_shutdown = false;
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
|
|
301
|
+
ctx.patched("wrongid");
|
|
302
|
+
ctx.timer(Duration::from_secs(1)).await;
|
|
303
|
+
Ok(().into())
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
worker
|
|
307
|
+
.submit_wf(
|
|
308
|
+
wf_id.to_owned(),
|
|
309
|
+
wf_type.to_owned(),
|
|
310
|
+
vec![],
|
|
311
|
+
WorkflowOptions::default(),
|
|
312
|
+
)
|
|
313
|
+
.await
|
|
314
|
+
.unwrap();
|
|
315
|
+
worker.run_until_done().await.unwrap();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -47,20 +47,6 @@ impl InternalFlags {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
#[cfg(test)]
|
|
51
|
-
pub fn all_core_enabled() -> Self {
|
|
52
|
-
Self {
|
|
53
|
-
enabled: true,
|
|
54
|
-
core: BTreeSet::from([
|
|
55
|
-
CoreInternalFlags::IdAndTypeDeterminismChecks,
|
|
56
|
-
CoreInternalFlags::UpsertSearchAttributeOnPatch,
|
|
57
|
-
]),
|
|
58
|
-
lang: Default::default(),
|
|
59
|
-
core_since_last_complete: Default::default(),
|
|
60
|
-
lang_since_last_complete: Default::default(),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
50
|
pub fn add_from_complete(&mut self, e: &WorkflowTaskCompletedEventAttributes) {
|
|
65
51
|
if !self.enabled {
|
|
66
52
|
return;
|
package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs
CHANGED
|
@@ -38,7 +38,7 @@ fsm! {
|
|
|
38
38
|
// upon observing a history event indicating that the command has been recorded. Note that this
|
|
39
39
|
// does not imply that the command has been _executed_, only that it _will be_ executed at some
|
|
40
40
|
// point in the future.
|
|
41
|
-
CommandIssued --(CommandRecorded
|
|
41
|
+
CommandIssued --(CommandRecorded, on_command_recorded) --> Done;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/// Instantiates an UpsertSearchAttributesMachine and packs it together with an initial command
|
|
@@ -62,37 +62,21 @@ pub(super) fn upsert_search_attrs(
|
|
|
62
62
|
);
|
|
63
63
|
// We must still create the command to preserve compatability with anyone previously doing
|
|
64
64
|
// this.
|
|
65
|
-
create_new(Default::default()
|
|
65
|
+
create_new(Default::default())
|
|
66
66
|
} else {
|
|
67
|
-
create_new(attribs.search_attributes.into()
|
|
67
|
+
create_new(attribs.search_attributes.into())
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/// May be used by other state machines / internal needs which desire upserting search attributes.
|
|
72
72
|
pub(super) fn upsert_search_attrs_internal(
|
|
73
73
|
attribs: UpsertWorkflowSearchAttributesCommandAttributes,
|
|
74
|
-
internal_flags: InternalFlagsRef,
|
|
75
74
|
) -> NewMachineWithCommand {
|
|
76
|
-
create_new(
|
|
77
|
-
attribs.search_attributes.unwrap_or_default(),
|
|
78
|
-
true,
|
|
79
|
-
internal_flags,
|
|
80
|
-
)
|
|
75
|
+
create_new(attribs.search_attributes.unwrap_or_default())
|
|
81
76
|
}
|
|
82
77
|
|
|
83
|
-
fn create_new(
|
|
84
|
-
|
|
85
|
-
should_skip_determinism: bool,
|
|
86
|
-
internal_flags: InternalFlagsRef,
|
|
87
|
-
) -> NewMachineWithCommand {
|
|
88
|
-
let sm = UpsertSearchAttributesMachine::from_parts(
|
|
89
|
-
Created {}.into(),
|
|
90
|
-
SharedState {
|
|
91
|
-
sa_map: sa_map.clone(),
|
|
92
|
-
should_skip_determinism,
|
|
93
|
-
internal_flags,
|
|
94
|
-
},
|
|
95
|
-
);
|
|
78
|
+
fn create_new(sa_map: SearchAttributes) -> NewMachineWithCommand {
|
|
79
|
+
let sm = UpsertSearchAttributesMachine::from_parts(Created {}.into(), SharedState {});
|
|
96
80
|
let cmd = Command {
|
|
97
81
|
command_type: CommandType::UpsertWorkflowSearchAttributes as i32,
|
|
98
82
|
attributes: Some(
|
|
@@ -110,11 +94,7 @@ fn create_new(
|
|
|
110
94
|
}
|
|
111
95
|
|
|
112
96
|
#[derive(Clone)]
|
|
113
|
-
pub(super) struct SharedState {
|
|
114
|
-
should_skip_determinism: bool,
|
|
115
|
-
sa_map: SearchAttributes,
|
|
116
|
-
internal_flags: InternalFlagsRef,
|
|
117
|
-
}
|
|
97
|
+
pub(super) struct SharedState {}
|
|
118
98
|
|
|
119
99
|
/// The state-machine-specific set of commands that are the results of state transition in the
|
|
120
100
|
/// UpsertSearchAttributesMachine. There are none of these because this state machine emits the
|
|
@@ -131,30 +111,9 @@ pub(super) struct Created {}
|
|
|
131
111
|
/// higher-level machinery, it transitions into this state.
|
|
132
112
|
#[derive(Debug, Default, Clone, derive_more::Display)]
|
|
133
113
|
pub(super) struct CommandIssued {}
|
|
134
|
-
pub(super) struct CmdRecDat {
|
|
135
|
-
sa_map: Option<SearchAttributes>,
|
|
136
|
-
replaying: bool,
|
|
137
|
-
}
|
|
138
114
|
|
|
139
115
|
impl CommandIssued {
|
|
140
|
-
pub(super) fn on_command_recorded(
|
|
141
|
-
self,
|
|
142
|
-
shared: &mut SharedState,
|
|
143
|
-
dat: CmdRecDat,
|
|
144
|
-
) -> UpsertSearchAttributesMachineTransition<Done> {
|
|
145
|
-
if shared.internal_flags.borrow_mut().try_use(
|
|
146
|
-
CoreInternalFlags::UpsertSearchAttributeOnPatch,
|
|
147
|
-
!dat.replaying,
|
|
148
|
-
) {
|
|
149
|
-
let sa = dat.sa_map.unwrap_or_default();
|
|
150
|
-
if !shared.should_skip_determinism && shared.sa_map != sa {
|
|
151
|
-
return TransitionResult::Err(WFMachinesError::Nondeterminism(format!(
|
|
152
|
-
"Search attribute upsert calls must remain deterministic, but {:?} does not \
|
|
153
|
-
match the attributes from history: {:?}",
|
|
154
|
-
shared.sa_map, sa
|
|
155
|
-
)));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
116
|
+
pub(super) fn on_command_recorded(self) -> UpsertSearchAttributesMachineTransition<Done> {
|
|
158
117
|
TransitionResult::default()
|
|
159
118
|
}
|
|
160
119
|
}
|
|
@@ -198,14 +157,9 @@ impl TryFrom<HistEventData> for UpsertSearchAttributesMachineEvents {
|
|
|
198
157
|
|
|
199
158
|
fn try_from(e: HistEventData) -> Result<Self, Self::Error> {
|
|
200
159
|
match e.event.attributes {
|
|
201
|
-
Some(history_event::Attributes::UpsertWorkflowSearchAttributesEventAttributes(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
CmdRecDat {
|
|
205
|
-
sa_map: attrs.search_attributes,
|
|
206
|
-
replaying: e.replaying,
|
|
207
|
-
},
|
|
208
|
-
)),
|
|
160
|
+
Some(history_event::Attributes::UpsertWorkflowSearchAttributesEventAttributes(_)) => {
|
|
161
|
+
Ok(UpsertSearchAttributesMachineEvents::CommandRecorded)
|
|
162
|
+
}
|
|
209
163
|
_ => Err(Self::Error::Nondeterminism(format!(
|
|
210
164
|
"UpsertWorkflowSearchAttributesMachine does not handle {e}"
|
|
211
165
|
))),
|
|
@@ -241,7 +195,6 @@ impl From<Created> for CommandIssued {
|
|
|
241
195
|
mod tests {
|
|
242
196
|
use super::{super::OnEventWrapper, *};
|
|
243
197
|
use crate::{
|
|
244
|
-
internal_flags::InternalFlags,
|
|
245
198
|
replay::TestHistoryBuilder,
|
|
246
199
|
test_help::{build_mock_pollers, mock_worker, MockPollCfg, ResponseType},
|
|
247
200
|
worker::{
|
|
@@ -249,8 +202,8 @@ mod tests {
|
|
|
249
202
|
workflow::{machines::patch_state_machine::VERSION_SEARCH_ATTR_KEY, ManagedWFFunc},
|
|
250
203
|
},
|
|
251
204
|
};
|
|
252
|
-
use rustfsm::
|
|
253
|
-
use std::
|
|
205
|
+
use rustfsm::StateMachine;
|
|
206
|
+
use std::collections::HashMap;
|
|
254
207
|
use temporal_sdk::{WfContext, WorkflowFunction};
|
|
255
208
|
use temporal_sdk_core_api::Worker;
|
|
256
209
|
use temporal_sdk_core_protos::{
|
|
@@ -317,25 +270,14 @@ mod tests {
|
|
|
317
270
|
}
|
|
318
271
|
|
|
319
272
|
#[rstest::rstest]
|
|
320
|
-
fn upsert_search_attrs_sm(
|
|
321
|
-
let mut sm = UpsertSearchAttributesMachine::from_parts(
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
},
|
|
328
|
-
);
|
|
329
|
-
|
|
330
|
-
let sa_attribs = if nondetermistic {
|
|
331
|
-
UpsertWorkflowSearchAttributesEventAttributes {
|
|
332
|
-
workflow_task_completed_event_id: 0,
|
|
333
|
-
search_attributes: Some(SearchAttributes {
|
|
334
|
-
indexed_fields: HashMap::from([("Yo".to_string(), Payload::default())]),
|
|
335
|
-
}),
|
|
336
|
-
}
|
|
337
|
-
} else {
|
|
338
|
-
Default::default()
|
|
273
|
+
fn upsert_search_attrs_sm() {
|
|
274
|
+
let mut sm = UpsertSearchAttributesMachine::from_parts(Created {}.into(), SharedState {});
|
|
275
|
+
|
|
276
|
+
let sa_attribs = UpsertWorkflowSearchAttributesEventAttributes {
|
|
277
|
+
workflow_task_completed_event_id: 0,
|
|
278
|
+
search_attributes: Some(SearchAttributes {
|
|
279
|
+
indexed_fields: HashMap::from([("Yo".to_string(), Payload::default())]),
|
|
280
|
+
}),
|
|
339
281
|
};
|
|
340
282
|
let recorded_history_event = HistoryEvent {
|
|
341
283
|
event_type: EventType::UpsertWorkflowSearchAttributes as i32,
|
|
@@ -365,15 +307,8 @@ mod tests {
|
|
|
365
307
|
assert_eq!(CommandIssued {}.to_string(), sm.state().to_string());
|
|
366
308
|
|
|
367
309
|
let recorded_res = OnEventWrapper::on_event_mut(&mut sm, cmd_recorded_sm_event);
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
recorded_res.unwrap_err(),
|
|
371
|
-
MachineError::Underlying(WFMachinesError::Nondeterminism(_))
|
|
372
|
-
);
|
|
373
|
-
} else {
|
|
374
|
-
recorded_res.expect("CommandRecorded should transition CommandIssued -> Done");
|
|
375
|
-
assert_eq!(Done {}.to_string(), sm.state().to_string());
|
|
376
|
-
}
|
|
310
|
+
recorded_res.expect("CommandRecorded should transition CommandIssued -> Done");
|
|
311
|
+
assert_eq!(Done {}.to_string(), sm.state().to_string());
|
|
377
312
|
}
|
|
378
313
|
|
|
379
314
|
#[rstest::rstest]
|
|
@@ -924,10 +924,7 @@ impl WorkflowMachines {
|
|
|
924
924
|
}
|
|
925
925
|
ProtoCmdAttrs::UpsertWorkflowSearchAttributesCommandAttributes(attrs) => {
|
|
926
926
|
self.add_cmd_to_wf_task(
|
|
927
|
-
upsert_search_attrs_internal(
|
|
928
|
-
attrs,
|
|
929
|
-
self.observed_internal_flags.clone(),
|
|
930
|
-
),
|
|
927
|
+
upsert_search_attrs_internal(attrs),
|
|
931
928
|
CommandIdKind::NeverResolves,
|
|
932
929
|
);
|
|
933
930
|
}
|
|
@@ -550,8 +550,15 @@ impl ManagedRun {
|
|
|
550
550
|
|
|
551
551
|
/// Delete the currently tracked workflow activation and return it, if any. Should be called
|
|
552
552
|
/// after the processing of the activation completion, and WFT reporting.
|
|
553
|
-
pub(super) fn delete_activation(
|
|
554
|
-
self
|
|
553
|
+
pub(super) fn delete_activation(
|
|
554
|
+
&mut self,
|
|
555
|
+
pred: impl FnOnce(&OutstandingActivation) -> bool,
|
|
556
|
+
) -> Option<OutstandingActivation> {
|
|
557
|
+
if self.activation().map(pred).unwrap_or_default() {
|
|
558
|
+
self.activation.take()
|
|
559
|
+
} else {
|
|
560
|
+
None
|
|
561
|
+
}
|
|
555
562
|
}
|
|
556
563
|
|
|
557
564
|
/// Called when local activities resolve
|
|
@@ -929,6 +936,7 @@ impl ManagedRun {
|
|
|
929
936
|
}
|
|
930
937
|
|
|
931
938
|
fn insert_outstanding_activation(&mut self, act: &ActivationOrAuto) {
|
|
939
|
+
warn!("Inserting {:?}", act);
|
|
932
940
|
let act_type = match &act {
|
|
933
941
|
ActivationOrAuto::LangActivation(act) | ActivationOrAuto::ReadyForQueries(act) => {
|
|
934
942
|
if act.is_legacy_query() {
|
|
@@ -23,8 +23,8 @@ pub(crate) use managed_run::ManagedWFFunc;
|
|
|
23
23
|
|
|
24
24
|
use crate::{
|
|
25
25
|
abstractions::{
|
|
26
|
-
stream_when_allowed, take_cell::TakeCell, MeteredSemaphore,
|
|
27
|
-
UsedMeteredSemPermit,
|
|
26
|
+
dbg_panic, stream_when_allowed, take_cell::TakeCell, MeteredSemaphore,
|
|
27
|
+
TrackedOwnedMeteredSemPermit, UsedMeteredSemPermit,
|
|
28
28
|
},
|
|
29
29
|
internal_flags::InternalFlags,
|
|
30
30
|
protosext::{legacy_query_failure, ValidPollWFTQResponse},
|
|
@@ -294,6 +294,7 @@ impl Workflows {
|
|
|
294
294
|
workflow_completion::Success::from_variants(vec![]).into(),
|
|
295
295
|
),
|
|
296
296
|
},
|
|
297
|
+
true,
|
|
297
298
|
// We need to say a type, but the type is irrelevant, so imagine some
|
|
298
299
|
// boxed function we'll never call.
|
|
299
300
|
Option::<Box<dyn Fn(PostActivateHookData) + Send>>::None,
|
|
@@ -309,6 +310,7 @@ impl Workflows {
|
|
|
309
310
|
run_id,
|
|
310
311
|
status: Some(auto_fail_to_complete_status(machines_err)),
|
|
311
312
|
},
|
|
313
|
+
true,
|
|
312
314
|
Option::<Box<dyn Fn(PostActivateHookData) + Send>>::None,
|
|
313
315
|
)
|
|
314
316
|
.await?;
|
|
@@ -324,8 +326,9 @@ impl Workflows {
|
|
|
324
326
|
pub(super) async fn activation_completed(
|
|
325
327
|
&self,
|
|
326
328
|
completion: WorkflowActivationCompletion,
|
|
329
|
+
is_autocomplete: bool,
|
|
327
330
|
post_activate_hook: Option<impl Fn(PostActivateHookData)>,
|
|
328
|
-
) -> Result<
|
|
331
|
+
) -> Result<(), CompleteWfError> {
|
|
329
332
|
let is_empty_completion = completion.is_empty();
|
|
330
333
|
let completion = validate_completion(completion)?;
|
|
331
334
|
let run_id = completion.run_id().to_string();
|
|
@@ -337,7 +340,7 @@ impl Workflows {
|
|
|
337
340
|
if !was_sent {
|
|
338
341
|
if is_empty_completion {
|
|
339
342
|
// Empty complete which is likely an evict reply, we can just ignore.
|
|
340
|
-
return Ok(
|
|
343
|
+
return Ok(());
|
|
341
344
|
}
|
|
342
345
|
panic!(
|
|
343
346
|
"A non-empty completion was not processed. Workflow processing may have \
|
|
@@ -345,9 +348,18 @@ impl Workflows {
|
|
|
345
348
|
);
|
|
346
349
|
}
|
|
347
350
|
|
|
348
|
-
let completion_outcome = rx
|
|
349
|
-
|
|
350
|
-
|
|
351
|
+
let completion_outcome = if let Ok(c) = rx.await {
|
|
352
|
+
c
|
|
353
|
+
} else {
|
|
354
|
+
dbg_panic!("Send half of activation complete response channel went missing");
|
|
355
|
+
self.request_eviction(
|
|
356
|
+
run_id,
|
|
357
|
+
"Send half of activation complete response channel went missing",
|
|
358
|
+
EvictionReason::Fatal,
|
|
359
|
+
);
|
|
360
|
+
return Ok(());
|
|
361
|
+
};
|
|
362
|
+
|
|
351
363
|
let mut wft_from_complete = None;
|
|
352
364
|
let wft_report_status = match completion_outcome.outcome {
|
|
353
365
|
ActivationCompleteOutcome::ReportWFTSuccess(report) => match report {
|
|
@@ -460,9 +472,10 @@ impl Workflows {
|
|
|
460
472
|
run_id,
|
|
461
473
|
wft_report_status,
|
|
462
474
|
wft_from_complete: maybe_pwft,
|
|
475
|
+
is_autocomplete,
|
|
463
476
|
});
|
|
464
477
|
|
|
465
|
-
Ok(
|
|
478
|
+
Ok(())
|
|
466
479
|
}
|
|
467
480
|
|
|
468
481
|
/// Tell workflow that a local activity has finished with the provided result
|
|
@@ -932,6 +945,7 @@ struct PostActivationMsg {
|
|
|
932
945
|
run_id: String,
|
|
933
946
|
wft_report_status: WFTReportStatus,
|
|
934
947
|
wft_from_complete: Option<(PreparedWFT, HistoryPaginator)>,
|
|
948
|
+
is_autocomplete: bool,
|
|
935
949
|
}
|
|
936
950
|
#[derive(Debug, Clone)]
|
|
937
951
|
#[cfg_attr(
|
|
@@ -338,37 +338,39 @@ impl WFStream {
|
|
|
338
338
|
|
|
339
339
|
// If we reported to server, we always want to mark it complete.
|
|
340
340
|
let maybe_t = self.complete_wft(run_id, report.wft_report_status);
|
|
341
|
-
// Delete the activation
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
.
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if let Some(
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
341
|
+
// Delete the activation, but only if the report came from lang, or we know the outstanding
|
|
342
|
+
// activation is expected to be completed internally.
|
|
343
|
+
if let Some(activation) = self.runs.get_mut(run_id).and_then(|rh| {
|
|
344
|
+
rh.delete_activation(|act| {
|
|
345
|
+
!report.is_autocomplete || matches!(act, OutstandingActivation::Autocomplete)
|
|
346
|
+
})
|
|
347
|
+
}) {
|
|
348
|
+
// Evict the run if the activation contained an eviction
|
|
349
|
+
let mut applied_buffered_poll_for_this_run = false;
|
|
350
|
+
if activation.has_eviction() {
|
|
351
|
+
debug!(run_id=%run_id, "Evicting run");
|
|
352
|
+
|
|
353
|
+
if let Some(mut rh) = self.runs.remove(run_id) {
|
|
354
|
+
if let Some(buff) = rh.take_buffered_wft() {
|
|
355
|
+
// Don't try to apply a buffered poll for this run if we just got a new WFT
|
|
356
|
+
// from completing, because by definition that buffered poll is now an
|
|
357
|
+
// out-of-date WFT.
|
|
358
|
+
if wft_from_complete.is_none() {
|
|
359
|
+
res = self.instantiate_or_update(buff);
|
|
360
|
+
applied_buffered_poll_for_this_run = true;
|
|
361
|
+
}
|
|
360
362
|
}
|
|
361
363
|
}
|
|
362
|
-
}
|
|
363
364
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
365
|
+
// Attempt to apply a buffered poll for some *other* run, if we didn't have a wft
|
|
366
|
+
// from complete or a buffered poll for *this* run.
|
|
367
|
+
if wft_from_complete.is_none() && !applied_buffered_poll_for_this_run {
|
|
368
|
+
if let Some(buff) = self.buffered_polls_need_cache_slot.pop_front() {
|
|
369
|
+
res = self.instantiate_or_update(buff);
|
|
370
|
+
}
|
|
369
371
|
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
372
374
|
|
|
373
375
|
if let Some((wft, pag)) = wft_from_complete {
|
|
374
376
|
debug!(run_id=%wft.execution.run_id, "New WFT from completion");
|
package/sdk-core/tests/main.rs
CHANGED
|
@@ -25,7 +25,7 @@ mod integ_tests {
|
|
|
25
25
|
use temporal_sdk_core_api::worker::WorkerConfigBuilder;
|
|
26
26
|
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::ListNamespacesRequest;
|
|
27
27
|
use temporal_sdk_core_test_utils::{
|
|
28
|
-
get_integ_server_options, get_integ_telem_options,
|
|
28
|
+
get_integ_server_options, get_integ_telem_options, init_integ_telem,
|
|
29
29
|
};
|
|
30
30
|
use url::Url;
|
|
31
31
|
|
|
@@ -58,35 +58,23 @@ mod integ_tests {
|
|
|
58
58
|
.await;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// https://github.com/temporalio/customization-samples/tree/master/tls/tls-simple
|
|
61
|
+
// Manually run to verify tls works against cloud. You will need certs in place in the
|
|
62
|
+
// indicated directory.
|
|
64
63
|
#[tokio::test]
|
|
65
64
|
#[ignore]
|
|
66
65
|
async fn tls_test() {
|
|
67
|
-
|
|
68
|
-
let root = tokio::fs::read(
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
.await
|
|
72
|
-
.unwrap();
|
|
73
|
-
let client_cert = tokio::fs::read(
|
|
74
|
-
"/home/sushi/dev/temporal/customization-samples/tls/tls-simple/certs/client.pem",
|
|
75
|
-
)
|
|
76
|
-
.await
|
|
77
|
-
.unwrap();
|
|
78
|
-
let client_private_key = tokio::fs::read(
|
|
79
|
-
"/home/sushi/dev/temporal/customization-samples/tls/tls-simple/certs/client.key",
|
|
80
|
-
)
|
|
81
|
-
.await
|
|
82
|
-
.unwrap();
|
|
66
|
+
init_integ_telem();
|
|
67
|
+
let root = tokio::fs::read("../.cloud_certs/ca.pem").await.unwrap();
|
|
68
|
+
let client_cert = tokio::fs::read("../.cloud_certs/client.pem").await.unwrap();
|
|
69
|
+
let client_private_key = tokio::fs::read("../.cloud_certs/client.key").await.unwrap();
|
|
83
70
|
let sgo = ClientOptionsBuilder::default()
|
|
84
|
-
.target_url(Url::from_str("https://
|
|
71
|
+
.target_url(Url::from_str("https://spencer.temporal-dev.tmprl.cloud:7233").unwrap())
|
|
85
72
|
.client_name("tls_tester")
|
|
86
73
|
.client_version("clientver")
|
|
87
74
|
.tls_cfg(TlsConfig {
|
|
88
75
|
server_root_ca_cert: Some(root),
|
|
89
|
-
|
|
76
|
+
// Not necessary, but illustrates functionality for people using proxies, etc.
|
|
77
|
+
domain: Some("spencer.temporal-dev.tmprl.cloud".to_string()),
|
|
90
78
|
client_tls_config: Some(ClientTlsConfig {
|
|
91
79
|
client_cert,
|
|
92
80
|
client_private_key,
|
|
@@ -95,9 +83,12 @@ mod integ_tests {
|
|
|
95
83
|
.build()
|
|
96
84
|
.unwrap();
|
|
97
85
|
let con = sgo
|
|
98
|
-
.connect(
|
|
86
|
+
.connect("spencer.temporal-dev".to_string(), None, None)
|
|
99
87
|
.await
|
|
100
88
|
.unwrap();
|
|
101
|
-
con
|
|
89
|
+
dbg!(con
|
|
90
|
+
.list_workflow_executions(100, vec![], "".to_string())
|
|
91
|
+
.await
|
|
92
|
+
.unwrap());
|
|
102
93
|
}
|
|
103
94
|
}
|