@temporalio/core-bridge 1.7.1 → 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.
Files changed (39) hide show
  1. package/Cargo.lock +21 -0
  2. package/package.json +3 -3
  3. package/releases/aarch64-apple-darwin/index.node +0 -0
  4. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  5. package/releases/x86_64-apple-darwin/index.node +0 -0
  6. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  7. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  8. package/sdk-core/client/src/raw.rs +15 -6
  9. package/sdk-core/core/Cargo.toml +1 -0
  10. package/sdk-core/core/src/core_tests/activity_tasks.rs +13 -5
  11. package/sdk-core/core/src/core_tests/workflow_tasks.rs +21 -39
  12. package/sdk-core/core/src/internal_flags.rs +132 -46
  13. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +10 -7
  14. package/sdk-core/core/src/worker/activities.rs +152 -142
  15. package/sdk-core/core/src/worker/client.rs +12 -8
  16. package/sdk-core/core/src/worker/mod.rs +7 -5
  17. package/sdk-core/core/src/worker/workflow/history_update.rs +86 -2
  18. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +4 -1
  19. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +5 -2
  20. package/sdk-core/core/src/worker/workflow/managed_run.rs +0 -1
  21. package/sdk-core/protos/api_upstream/.github/workflows/publish-docs.yml +23 -0
  22. package/sdk-core/protos/api_upstream/Makefile +1 -1
  23. package/sdk-core/protos/api_upstream/buf.yaml +5 -0
  24. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +17 -0
  25. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  26. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +6 -3
  27. package/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +1 -1
  28. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +12 -22
  29. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +2 -2
  30. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -0
  31. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +145 -48
  32. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +19 -8
  33. package/sdk-core/test-utils/src/lib.rs +29 -7
  34. package/sdk-core/tests/integ_tests/activity_functions.rs +5 -0
  35. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +2 -4
  36. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +0 -1
  37. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +5 -7
  38. package/sdk-core/tests/integ_tests/workflow_tests.rs +3 -7
  39. package/sdk-core/tests/main.rs +1 -0
package/Cargo.lock CHANGED
@@ -517,6 +517,26 @@ dependencies = [
517
517
  "cfg-if",
518
518
  ]
519
519
 
520
+ [[package]]
521
+ name = "enum-iterator"
522
+ version = "1.4.0"
523
+ source = "registry+https://github.com/rust-lang/crates.io-index"
524
+ checksum = "706d9e7cf1c7664859d79cd524e4e53ea2b67ea03c98cc2870c5e539695d597e"
525
+ dependencies = [
526
+ "enum-iterator-derive",
527
+ ]
528
+
529
+ [[package]]
530
+ name = "enum-iterator-derive"
531
+ version = "1.2.0"
532
+ source = "registry+https://github.com/rust-lang/crates.io-index"
533
+ checksum = "355f93763ef7b0ae1c43c4d8eccc9d5848d84ad1a1d8ce61c421d1ac85a19d05"
534
+ dependencies = [
535
+ "proc-macro2",
536
+ "quote",
537
+ "syn 1.0.109",
538
+ ]
539
+
520
540
  [[package]]
521
541
  name = "enum_dispatch"
522
542
  version = "0.3.11"
@@ -2272,6 +2292,7 @@ dependencies = [
2272
2292
  "dashmap",
2273
2293
  "derive_builder",
2274
2294
  "derive_more",
2295
+ "enum-iterator",
2275
2296
  "enum_dispatch",
2276
2297
  "flate2",
2277
2298
  "futures",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temporalio/core-bridge",
3
- "version": "1.7.1",
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.1",
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": "368aa83c631555fc31cff25f4af8817d069878d8"
56
+ "gitHead": "779561124eecdec8396e658c0a1305d343dfaff7"
57
57
  }
@@ -740,9 +740,9 @@ proxier! {
740
740
  }
741
741
  );
742
742
  (
743
- update_worker_build_id_ordering,
744
- UpdateWorkerBuildIdOrderingRequest,
745
- UpdateWorkerBuildIdOrderingResponse,
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
- get_worker_build_id_ordering,
754
- GetWorkerBuildIdOrderingRequest,
755
- GetWorkerBuildIdOrderingResponse,
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,
@@ -28,6 +28,7 @@ dashmap = "5.0"
28
28
  derive_builder = "0.12"
29
29
  derive_more = "0.99"
30
30
  enum_dispatch = "0.3"
31
+ enum-iterator = "1.4"
31
32
  flate2 = "1.0"
32
33
  futures = "0.3"
33
34
  futures-util = "0.3"
@@ -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
- tokio::join! {
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
- tokio::join!(run_fut, act_completer);
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(Duration::from_millis(500))
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
 
@@ -1,5 +1,7 @@
1
1
  use crate::{
2
- advance_fut, job_assert,
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::{AtomicBool, AtomicU64, Ordering},
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
- Ok(really_empty_fetch_resp.clone())
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 to server when they're used
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.add_full_wf_task();
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), ResponseType::ToTaskNum(2)],
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
- if !first_poll.load(Ordering::Acquire) {
2408
- assert_matches!(c.sdk_metadata.core_used_flags.as_slice(), &[1]);
2409
- }
2410
- first_poll.store(false, Ordering::Release);
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 std::collections::{BTreeSet, HashSet};
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,64 +32,85 @@ pub(crate) enum CoreInternalFlags {
28
32
  }
29
33
 
30
34
  #[derive(Debug, Clone, PartialEq, Eq)]
31
- pub(crate) struct InternalFlags {
32
- enabled: bool,
33
- core: BTreeSet<CoreInternalFlags>,
34
- lang: BTreeSet<u32>,
35
- core_since_last_complete: HashSet<CoreInternalFlags>,
36
- lang_since_last_complete: HashSet<u32>,
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
- Self {
42
- enabled: server_capabilities.sdk_metadata,
43
- core: Default::default(),
44
- lang: Default::default(),
45
- core_since_last_complete: Default::default(),
46
- 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,
47
55
  }
48
56
  }
49
57
 
50
58
  pub fn add_from_complete(&mut self, e: &WorkflowTaskCompletedEventAttributes) {
51
- if !self.enabled {
52
- return;
53
- }
54
-
55
- if let Some(metadata) = e.sdk_metadata.as_ref() {
56
- self.core.extend(
57
- metadata
58
- .core_used_flags
59
- .iter()
60
- .map(|u| CoreInternalFlags::from_u32(*u)),
61
- );
62
- 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
+ }
63
69
  }
64
70
  }
65
71
 
66
72
  pub fn add_lang_used(&mut self, flags: impl IntoIterator<Item = u32>) {
67
- if !self.enabled {
68
- return;
73
+ if let Self::Enabled {
74
+ lang_since_last_complete,
75
+ ..
76
+ } = self
77
+ {
78
+ lang_since_last_complete.extend(flags.into_iter());
69
79
  }
70
-
71
- self.lang_since_last_complete.extend(flags.into_iter());
72
80
  }
73
81
 
74
82
  /// Returns true if this flag may currently be used. If `should_record` is true, always returns
75
83
  /// true and records the flag as being used, for taking later via
76
84
  /// [Self::gather_for_wft_complete].
77
85
  pub fn try_use(&mut self, core_patch: CoreInternalFlags, should_record: bool) -> bool {
78
- if !self.enabled {
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
+ }
79
99
  // If the server does not support the metadata field, we must assume we can never use
80
100
  // any internal flags since they can't be recorded for future use
81
- return false;
101
+ Self::Disabled => false,
82
102
  }
103
+ }
83
104
 
84
- if should_record {
85
- self.core_since_last_complete.insert(core_patch);
86
- true
87
- } else {
88
- self.core.contains(&core_patch)
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());
89
114
  }
90
115
  }
91
116
 
@@ -93,18 +118,39 @@ impl InternalFlags {
93
118
  /// sdk metadata message that can be combined with any existing data before sending the WFT
94
119
  /// complete
95
120
  pub fn gather_for_wft_complete(&mut self) -> WorkflowTaskCompletedMetadata {
96
- WorkflowTaskCompletedMetadata {
97
- core_used_flags: self
98
- .core_since_last_complete
99
- .drain()
100
- .map(|p| p as u32)
101
- .collect(),
102
- lang_used_flags: self.lang_since_last_complete.drain().collect(),
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(),
103
146
  }
104
147
  }
105
148
 
106
- pub fn all_lang(&self) -> &BTreeSet<u32> {
107
- &self.lang
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
+ }
108
154
  }
109
155
  }
110
156
 
@@ -116,6 +162,11 @@ impl CoreInternalFlags {
116
162
  _ => Self::TooHigh,
117
163
  }
118
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
+ }
119
170
  }
120
171
 
121
172
  #[cfg(test)]
@@ -138,4 +189,39 @@ mod tests {
138
189
  assert_matches!(gathered.core_used_flags.as_slice(), &[]);
139
190
  assert_matches!(gathered.lang_used_flags.as_slice(), &[]);
140
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
+ }
141
227
  }
@@ -1,11 +1,14 @@
1
- use crate::abstractions::MeteredSemaphore;
2
- use crate::worker::activities::PermittedTqResp;
3
- use crate::{pollers::BoxedActPoller, MetricsContext};
1
+ use crate::{
2
+ abstractions::MeteredSemaphore, pollers::BoxedActPoller, worker::activities::PermittedTqResp,
3
+ MetricsContext,
4
+ };
4
5
  use futures::{stream, Stream};
5
- use governor::clock::DefaultClock;
6
- use governor::middleware::NoOpMiddleware;
7
- use governor::state::{InMemoryState, NotKeyed};
8
- use governor::RateLimiter;
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;