@temporalio/core-bridge 1.7.0 → 1.7.2
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 +500 -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/client/src/raw.rs +15 -6
- package/sdk-core/core/Cargo.toml +1 -0
- package/sdk-core/core/src/core_tests/activity_tasks.rs +13 -5
- package/sdk-core/core/src/core_tests/determinism.rs +49 -2
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +21 -39
- package/sdk-core/core/src/internal_flags.rs +132 -60
- package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +10 -7
- package/sdk-core/core/src/worker/activities.rs +152 -142
- package/sdk-core/core/src/worker/client.rs +12 -8
- package/sdk-core/core/src/worker/mod.rs +8 -5
- package/sdk-core/core/src/worker/workflow/history_update.rs +86 -2
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +4 -1
- 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 +6 -6
- package/sdk-core/core/src/worker/workflow/managed_run.rs +9 -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/protos/api_upstream/.github/workflows/publish-docs.yml +23 -0
- package/sdk-core/protos/api_upstream/Makefile +1 -1
- package/sdk-core/protos/api_upstream/buf.yaml +5 -0
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +17 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +6 -3
- package/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +1 -1
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +12 -22
- package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +2 -2
- package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +145 -48
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +19 -8
- package/sdk-core/test-utils/src/lib.rs +29 -7
- package/sdk-core/tests/integ_tests/activity_functions.rs +5 -0
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +2 -4
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +0 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +5 -7
- package/sdk-core/tests/integ_tests/workflow_tests.rs +3 -7
- package/sdk-core/tests/main.rs +16 -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.2",
|
|
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.2",
|
|
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": "779561124eecdec8396e658c0a1305d343dfaff7"
|
|
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
|
}
|
|
@@ -740,9 +740,9 @@ proxier! {
|
|
|
740
740
|
}
|
|
741
741
|
);
|
|
742
742
|
(
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
743
|
+
update_worker_build_id_compatibility,
|
|
744
|
+
UpdateWorkerBuildIdCompatibilityRequest,
|
|
745
|
+
UpdateWorkerBuildIdCompatibilityResponse,
|
|
746
746
|
|r| {
|
|
747
747
|
let mut labels = AttachMetricLabels::namespace(r.get_ref().namespace.clone());
|
|
748
748
|
labels.task_q_str(r.get_ref().task_queue.clone());
|
|
@@ -750,9 +750,9 @@ proxier! {
|
|
|
750
750
|
}
|
|
751
751
|
);
|
|
752
752
|
(
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
753
|
+
get_worker_build_id_compatibility,
|
|
754
|
+
GetWorkerBuildIdCompatibilityRequest,
|
|
755
|
+
GetWorkerBuildIdCompatibilityResponse,
|
|
756
756
|
|r| {
|
|
757
757
|
let mut labels = AttachMetricLabels::namespace(r.get_ref().namespace.clone());
|
|
758
758
|
labels.task_q_str(r.get_ref().task_queue.clone());
|
|
@@ -768,6 +768,15 @@ proxier! {
|
|
|
768
768
|
r.extensions_mut().insert(labels);
|
|
769
769
|
}
|
|
770
770
|
);
|
|
771
|
+
(
|
|
772
|
+
poll_workflow_execution_update,
|
|
773
|
+
PollWorkflowExecutionUpdateRequest,
|
|
774
|
+
PollWorkflowExecutionUpdateResponse,
|
|
775
|
+
|r| {
|
|
776
|
+
let labels = AttachMetricLabels::namespace(r.get_ref().namespace.clone());
|
|
777
|
+
r.extensions_mut().insert(labels);
|
|
778
|
+
}
|
|
779
|
+
);
|
|
771
780
|
(
|
|
772
781
|
start_batch_operation,
|
|
773
782
|
StartBatchOperationRequest,
|
package/sdk-core/core/Cargo.toml
CHANGED
|
@@ -58,7 +58,7 @@ use temporal_sdk_core_protos::{
|
|
|
58
58
|
TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE,
|
|
59
59
|
};
|
|
60
60
|
use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd, TestWorker};
|
|
61
|
-
use tokio::{sync::Barrier, time::sleep};
|
|
61
|
+
use tokio::{join, sync::Barrier, time::sleep};
|
|
62
62
|
use tokio_util::sync::CancellationToken;
|
|
63
63
|
|
|
64
64
|
fn three_tasks() -> VecDeque<PollActivityTaskQueueResponse> {
|
|
@@ -288,7 +288,7 @@ async fn activity_cancel_interrupts_poll() {
|
|
|
288
288
|
// Perform first poll to get the activity registered
|
|
289
289
|
let act = core.poll_activity_task().await.unwrap();
|
|
290
290
|
// Poll should block until heartbeat is sent, issuing the cancel, and interrupting the poll
|
|
291
|
-
|
|
291
|
+
join! {
|
|
292
292
|
async {
|
|
293
293
|
core.record_activity_heartbeat(ActivityHeartbeat {
|
|
294
294
|
task_token: act.task_token,
|
|
@@ -984,7 +984,7 @@ async fn activity_tasks_from_completion_reserve_slots() {
|
|
|
984
984
|
// This wf poll should *not* set the flag that it wants tasks back since both slots are
|
|
985
985
|
// occupied
|
|
986
986
|
let run_fut = async { worker.run_until_done().await.unwrap() };
|
|
987
|
-
|
|
987
|
+
join!(run_fut, act_completer);
|
|
988
988
|
}
|
|
989
989
|
|
|
990
990
|
#[tokio::test]
|
|
@@ -1052,9 +1052,11 @@ async fn cant_complete_activity_with_unset_result_payload() {
|
|
|
1052
1052
|
)
|
|
1053
1053
|
}
|
|
1054
1054
|
|
|
1055
|
+
#[rstest::rstest]
|
|
1055
1056
|
#[tokio::test]
|
|
1056
|
-
async fn graceful_shutdown() {
|
|
1057
|
+
async fn graceful_shutdown(#[values(true, false)] at_max_outstanding: bool) {
|
|
1057
1058
|
let _task_q = "q";
|
|
1059
|
+
let grace_period = Duration::from_millis(200);
|
|
1058
1060
|
let mut tasks = three_tasks();
|
|
1059
1061
|
let mut mock_client = mock_workflow_client();
|
|
1060
1062
|
mock_client
|
|
@@ -1067,15 +1069,21 @@ async fn graceful_shutdown() {
|
|
|
1067
1069
|
.times(3)
|
|
1068
1070
|
.returning(|_, _| Ok(Default::default()));
|
|
1069
1071
|
|
|
1072
|
+
let max_outstanding = if at_max_outstanding { 3_usize } else { 100 };
|
|
1070
1073
|
let worker = Worker::new_test(
|
|
1071
1074
|
test_worker_cfg()
|
|
1072
|
-
.graceful_shutdown_period(
|
|
1075
|
+
.graceful_shutdown_period(grace_period)
|
|
1076
|
+
.max_outstanding_activities(max_outstanding)
|
|
1073
1077
|
.build()
|
|
1074
1078
|
.unwrap(),
|
|
1075
1079
|
mock_client,
|
|
1076
1080
|
);
|
|
1077
1081
|
|
|
1078
1082
|
let _1 = worker.poll_activity_task().await.unwrap();
|
|
1083
|
+
|
|
1084
|
+
// Wait at least the grace period after one poll - ensuring it doesn't trigger prematurely
|
|
1085
|
+
tokio::time::sleep(grace_period.mul_f32(1.1)).await;
|
|
1086
|
+
|
|
1079
1087
|
let _2 = worker.poll_activity_task().await.unwrap();
|
|
1080
1088
|
let _3 = worker.poll_activity_task().await.unwrap();
|
|
1081
1089
|
|
|
@@ -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
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
-
advance_fut,
|
|
2
|
+
advance_fut,
|
|
3
|
+
internal_flags::CoreInternalFlags,
|
|
4
|
+
job_assert,
|
|
3
5
|
replay::TestHistoryBuilder,
|
|
4
6
|
test_help::{
|
|
5
7
|
build_fake_worker, build_mock_pollers, build_multihist_mock_sg, canned_histories,
|
|
@@ -14,9 +16,9 @@ use crate::{
|
|
|
14
16
|
use futures::{stream, FutureExt};
|
|
15
17
|
use rstest::{fixture, rstest};
|
|
16
18
|
use std::{
|
|
17
|
-
collections::{HashMap, VecDeque},
|
|
19
|
+
collections::{HashMap, HashSet, VecDeque},
|
|
18
20
|
sync::{
|
|
19
|
-
atomic::{
|
|
21
|
+
atomic::{AtomicU64, Ordering},
|
|
20
22
|
Arc,
|
|
21
23
|
},
|
|
22
24
|
time::Duration,
|
|
@@ -54,9 +56,7 @@ use temporal_sdk_core_protos::{
|
|
|
54
56
|
},
|
|
55
57
|
DEFAULT_ACTIVITY_TYPE, DEFAULT_WORKFLOW_TYPE,
|
|
56
58
|
};
|
|
57
|
-
use temporal_sdk_core_test_utils::{
|
|
58
|
-
fanout_tasks, schedule_activity_cmd, start_timer_cmd, WorkerTestHelpers,
|
|
59
|
-
};
|
|
59
|
+
use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd, WorkerTestHelpers};
|
|
60
60
|
use tokio::{
|
|
61
61
|
join,
|
|
62
62
|
sync::{Barrier, Semaphore},
|
|
@@ -2297,7 +2297,7 @@ async fn ensure_fetching_fail_during_complete_sends_task_failure() {
|
|
|
2297
2297
|
mock.expect_get_workflow_execution_history()
|
|
2298
2298
|
.returning(move |_, _, _| {
|
|
2299
2299
|
error!("Called fetch second time!");
|
|
2300
|
-
|
|
2300
|
+
Err(tonic::Status::not_found("Ahh broken"))
|
|
2301
2301
|
})
|
|
2302
2302
|
.times(1);
|
|
2303
2303
|
mock.expect_fail_workflow_task()
|
|
@@ -2380,54 +2380,36 @@ async fn lang_internal_flags() {
|
|
|
2380
2380
|
core.shutdown().await;
|
|
2381
2381
|
}
|
|
2382
2382
|
|
|
2383
|
-
// Verify we send flags
|
|
2383
|
+
// Verify we send all core internal flags on the first non-replay WFT
|
|
2384
2384
|
#[tokio::test]
|
|
2385
2385
|
async fn core_internal_flags() {
|
|
2386
2386
|
let mut t = TestHistoryBuilder::default();
|
|
2387
2387
|
t.add_by_type(EventType::WorkflowExecutionStarted);
|
|
2388
|
-
t.
|
|
2389
|
-
let act_scheduled_event_id = t.add_activity_task_scheduled("act-id");
|
|
2390
|
-
let act_started_event_id = t.add_activity_task_started(act_scheduled_event_id);
|
|
2391
|
-
t.add_activity_task_completed(
|
|
2392
|
-
act_scheduled_event_id,
|
|
2393
|
-
act_started_event_id,
|
|
2394
|
-
Default::default(),
|
|
2395
|
-
);
|
|
2396
|
-
t.add_full_wf_task();
|
|
2397
|
-
t.add_workflow_execution_completed();
|
|
2388
|
+
t.add_workflow_task_scheduled_and_started();
|
|
2398
2389
|
|
|
2399
2390
|
let mut mh = MockPollCfg::from_resp_batches(
|
|
2400
2391
|
"fake_wf_id",
|
|
2401
2392
|
t,
|
|
2402
|
-
[ResponseType::ToTaskNum(1)
|
|
2393
|
+
[ResponseType::ToTaskNum(1)],
|
|
2403
2394
|
mock_workflow_client(),
|
|
2404
2395
|
);
|
|
2405
|
-
let first_poll = AtomicBool::new(true);
|
|
2406
2396
|
mh.completion_asserts = Some(Box::new(move |c| {
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2397
|
+
assert_eq!(
|
|
2398
|
+
c.sdk_metadata
|
|
2399
|
+
.core_used_flags
|
|
2400
|
+
.iter()
|
|
2401
|
+
.copied()
|
|
2402
|
+
.collect::<HashSet<_>>(),
|
|
2403
|
+
CoreInternalFlags::all_except_too_high()
|
|
2404
|
+
.into_iter()
|
|
2405
|
+
.map(|f| f as u32)
|
|
2406
|
+
.collect()
|
|
2407
|
+
);
|
|
2411
2408
|
}));
|
|
2412
2409
|
let mut mock = build_mock_pollers(mh);
|
|
2413
2410
|
mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
|
|
2414
2411
|
let core = mock_worker(mock);
|
|
2415
2412
|
|
|
2416
|
-
let act = core.poll_workflow_activation().await.unwrap();
|
|
2417
|
-
core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
|
|
2418
|
-
act.run_id,
|
|
2419
|
-
schedule_activity_cmd(
|
|
2420
|
-
1,
|
|
2421
|
-
"whatever",
|
|
2422
|
-
"act-id",
|
|
2423
|
-
ActivityCancellationType::TryCancel,
|
|
2424
|
-
Duration::from_secs(60),
|
|
2425
|
-
Duration::from_secs(60),
|
|
2426
|
-
),
|
|
2427
|
-
))
|
|
2428
|
-
.await
|
|
2429
|
-
.unwrap();
|
|
2430
|
-
|
|
2431
2413
|
let act = core.poll_workflow_activation().await.unwrap();
|
|
2432
2414
|
core.complete_execution(&act.run_id).await;
|
|
2433
2415
|
core.shutdown().await;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
//! Utilities for and tracking of internal versions which alter history in incompatible ways
|
|
2
2
|
//! so that we can use older code paths for workflows executed on older core versions.
|
|
3
3
|
|
|
4
|
-
use
|
|
4
|
+
use itertools::Either;
|
|
5
|
+
use std::{
|
|
6
|
+
collections::{BTreeSet, HashSet},
|
|
7
|
+
iter,
|
|
8
|
+
};
|
|
5
9
|
use temporal_sdk_core_protos::temporal::api::{
|
|
6
10
|
history::v1::WorkflowTaskCompletedEventAttributes, sdk::v1::WorkflowTaskCompletedMetadata,
|
|
7
11
|
workflowservice::v1::get_system_info_response,
|
|
@@ -15,7 +19,7 @@ use temporal_sdk_core_protos::temporal::api::{
|
|
|
15
19
|
/// that removing older variants does not create any change in existing values. Removed flag
|
|
16
20
|
/// variants must be reserved forever (a-la protobuf), and should be called out in a comment.
|
|
17
21
|
#[repr(u32)]
|
|
18
|
-
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug)]
|
|
22
|
+
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone, Debug, enum_iterator::Sequence)]
|
|
19
23
|
pub(crate) enum CoreInternalFlags {
|
|
20
24
|
/// In this flag additional checks were added to a number of state machines to ensure that
|
|
21
25
|
/// the ID and type of activities, local activities, and child workflows match during replay.
|
|
@@ -28,78 +32,85 @@ pub(crate) enum CoreInternalFlags {
|
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
31
|
-
pub(crate)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
pub(crate) enum InternalFlags {
|
|
36
|
+
Enabled {
|
|
37
|
+
core: BTreeSet<CoreInternalFlags>,
|
|
38
|
+
lang: BTreeSet<u32>,
|
|
39
|
+
core_since_last_complete: HashSet<CoreInternalFlags>,
|
|
40
|
+
lang_since_last_complete: HashSet<u32>,
|
|
41
|
+
},
|
|
42
|
+
Disabled,
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
impl InternalFlags {
|
|
40
46
|
pub fn new(server_capabilities: &get_system_info_response::Capabilities) -> Self {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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(),
|
|
47
|
+
match server_capabilities.sdk_metadata {
|
|
48
|
+
true => Self::Enabled {
|
|
49
|
+
core: Default::default(),
|
|
50
|
+
lang: Default::default(),
|
|
51
|
+
core_since_last_complete: Default::default(),
|
|
52
|
+
lang_since_last_complete: Default::default(),
|
|
53
|
+
},
|
|
54
|
+
false => Self::Disabled,
|
|
61
55
|
}
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
pub fn add_from_complete(&mut self, e: &WorkflowTaskCompletedEventAttributes) {
|
|
65
|
-
if
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
);
|
|
76
|
-
self.lang.extend(metadata.lang_used_flags.iter());
|
|
59
|
+
if let Self::Enabled { core, lang, .. } = self {
|
|
60
|
+
if let Some(metadata) = e.sdk_metadata.as_ref() {
|
|
61
|
+
core.extend(
|
|
62
|
+
metadata
|
|
63
|
+
.core_used_flags
|
|
64
|
+
.iter()
|
|
65
|
+
.map(|u| CoreInternalFlags::from_u32(*u)),
|
|
66
|
+
);
|
|
67
|
+
lang.extend(metadata.lang_used_flags.iter());
|
|
68
|
+
}
|
|
77
69
|
}
|
|
78
70
|
}
|
|
79
71
|
|
|
80
72
|
pub fn add_lang_used(&mut self, flags: impl IntoIterator<Item = u32>) {
|
|
81
|
-
if
|
|
82
|
-
|
|
73
|
+
if let Self::Enabled {
|
|
74
|
+
lang_since_last_complete,
|
|
75
|
+
..
|
|
76
|
+
} = self
|
|
77
|
+
{
|
|
78
|
+
lang_since_last_complete.extend(flags.into_iter());
|
|
83
79
|
}
|
|
84
|
-
|
|
85
|
-
self.lang_since_last_complete.extend(flags.into_iter());
|
|
86
80
|
}
|
|
87
81
|
|
|
88
82
|
/// Returns true if this flag may currently be used. If `should_record` is true, always returns
|
|
89
83
|
/// true and records the flag as being used, for taking later via
|
|
90
84
|
/// [Self::gather_for_wft_complete].
|
|
91
85
|
pub fn try_use(&mut self, core_patch: CoreInternalFlags, should_record: bool) -> bool {
|
|
92
|
-
|
|
86
|
+
match self {
|
|
87
|
+
Self::Enabled {
|
|
88
|
+
core,
|
|
89
|
+
core_since_last_complete,
|
|
90
|
+
..
|
|
91
|
+
} => {
|
|
92
|
+
if should_record {
|
|
93
|
+
core_since_last_complete.insert(core_patch);
|
|
94
|
+
true
|
|
95
|
+
} else {
|
|
96
|
+
core.contains(&core_patch)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
93
99
|
// If the server does not support the metadata field, we must assume we can never use
|
|
94
100
|
// any internal flags since they can't be recorded for future use
|
|
95
|
-
|
|
101
|
+
Self::Disabled => false,
|
|
96
102
|
}
|
|
103
|
+
}
|
|
97
104
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
/// Writes all known core flags to the set which should be recorded in the current WFT if not
|
|
106
|
+
/// already known. Must only be called if not replaying.
|
|
107
|
+
pub fn write_all_known(&mut self) {
|
|
108
|
+
if let Self::Enabled {
|
|
109
|
+
core_since_last_complete,
|
|
110
|
+
..
|
|
111
|
+
} = self
|
|
112
|
+
{
|
|
113
|
+
core_since_last_complete.extend(CoreInternalFlags::all_except_too_high());
|
|
103
114
|
}
|
|
104
115
|
}
|
|
105
116
|
|
|
@@ -107,18 +118,39 @@ impl InternalFlags {
|
|
|
107
118
|
/// sdk metadata message that can be combined with any existing data before sending the WFT
|
|
108
119
|
/// complete
|
|
109
120
|
pub fn gather_for_wft_complete(&mut self) -> WorkflowTaskCompletedMetadata {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
match self {
|
|
122
|
+
Self::Enabled {
|
|
123
|
+
core_since_last_complete,
|
|
124
|
+
lang_since_last_complete,
|
|
125
|
+
core,
|
|
126
|
+
lang,
|
|
127
|
+
} => {
|
|
128
|
+
let core_newly_used: Vec<_> = core_since_last_complete
|
|
129
|
+
.iter()
|
|
130
|
+
.filter(|f| !core.contains(f))
|
|
131
|
+
.map(|p| *p as u32)
|
|
132
|
+
.collect();
|
|
133
|
+
let lang_newly_used: Vec<_> = lang_since_last_complete
|
|
134
|
+
.iter()
|
|
135
|
+
.filter(|f| !lang.contains(f))
|
|
136
|
+
.copied()
|
|
137
|
+
.collect();
|
|
138
|
+
core.extend(core_since_last_complete.iter());
|
|
139
|
+
lang.extend(lang_since_last_complete.iter());
|
|
140
|
+
WorkflowTaskCompletedMetadata {
|
|
141
|
+
core_used_flags: core_newly_used,
|
|
142
|
+
lang_used_flags: lang_newly_used,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
Self::Disabled => WorkflowTaskCompletedMetadata::default(),
|
|
117
146
|
}
|
|
118
147
|
}
|
|
119
148
|
|
|
120
|
-
pub fn all_lang(&self) ->
|
|
121
|
-
|
|
149
|
+
pub fn all_lang(&self) -> impl Iterator<Item = u32> + '_ {
|
|
150
|
+
match self {
|
|
151
|
+
Self::Enabled { lang, .. } => Either::Left(lang.iter().copied()),
|
|
152
|
+
Self::Disabled => Either::Right(iter::empty()),
|
|
153
|
+
}
|
|
122
154
|
}
|
|
123
155
|
}
|
|
124
156
|
|
|
@@ -130,6 +162,11 @@ impl CoreInternalFlags {
|
|
|
130
162
|
_ => Self::TooHigh,
|
|
131
163
|
}
|
|
132
164
|
}
|
|
165
|
+
|
|
166
|
+
pub fn all_except_too_high() -> impl Iterator<Item = CoreInternalFlags> {
|
|
167
|
+
enum_iterator::all::<CoreInternalFlags>()
|
|
168
|
+
.filter(|f| !matches!(f, CoreInternalFlags::TooHigh))
|
|
169
|
+
}
|
|
133
170
|
}
|
|
134
171
|
|
|
135
172
|
#[cfg(test)]
|
|
@@ -152,4 +189,39 @@ mod tests {
|
|
|
152
189
|
assert_matches!(gathered.core_used_flags.as_slice(), &[]);
|
|
153
190
|
assert_matches!(gathered.lang_used_flags.as_slice(), &[]);
|
|
154
191
|
}
|
|
192
|
+
|
|
193
|
+
#[test]
|
|
194
|
+
fn all_have_u32_from_impl() {
|
|
195
|
+
let all_known = CoreInternalFlags::all_except_too_high();
|
|
196
|
+
for flag in all_known {
|
|
197
|
+
let as_u32 = flag as u32;
|
|
198
|
+
assert_eq!(CoreInternalFlags::from_u32(as_u32), flag);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#[test]
|
|
203
|
+
fn only_writes_new_flags() {
|
|
204
|
+
let mut f = InternalFlags::new(&Capabilities {
|
|
205
|
+
sdk_metadata: true,
|
|
206
|
+
..Default::default()
|
|
207
|
+
});
|
|
208
|
+
f.add_lang_used([1]);
|
|
209
|
+
f.try_use(CoreInternalFlags::IdAndTypeDeterminismChecks, true);
|
|
210
|
+
let gathered = f.gather_for_wft_complete();
|
|
211
|
+
assert_matches!(gathered.core_used_flags.as_slice(), &[1]);
|
|
212
|
+
assert_matches!(gathered.lang_used_flags.as_slice(), &[1]);
|
|
213
|
+
|
|
214
|
+
f.add_from_complete(&WorkflowTaskCompletedEventAttributes {
|
|
215
|
+
sdk_metadata: Some(WorkflowTaskCompletedMetadata {
|
|
216
|
+
core_used_flags: vec![2],
|
|
217
|
+
lang_used_flags: vec![2],
|
|
218
|
+
}),
|
|
219
|
+
..Default::default()
|
|
220
|
+
});
|
|
221
|
+
f.add_lang_used([2]);
|
|
222
|
+
f.try_use(CoreInternalFlags::UpsertSearchAttributeOnPatch, true);
|
|
223
|
+
let gathered = f.gather_for_wft_complete();
|
|
224
|
+
assert_matches!(gathered.core_used_flags.as_slice(), &[]);
|
|
225
|
+
assert_matches!(gathered.lang_used_flags.as_slice(), &[]);
|
|
226
|
+
}
|
|
155
227
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
use crate::
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
use crate::{
|
|
2
|
+
abstractions::MeteredSemaphore, pollers::BoxedActPoller, worker::activities::PermittedTqResp,
|
|
3
|
+
MetricsContext,
|
|
4
|
+
};
|
|
4
5
|
use futures::{stream, Stream};
|
|
5
|
-
use governor::
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
use governor::{
|
|
7
|
+
clock::DefaultClock,
|
|
8
|
+
middleware::NoOpMiddleware,
|
|
9
|
+
state::{InMemoryState, NotKeyed},
|
|
10
|
+
RateLimiter,
|
|
11
|
+
};
|
|
9
12
|
use std::sync::Arc;
|
|
10
13
|
use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
|
|
11
14
|
use tokio::select;
|