@temporalio/core-bridge 1.9.0 → 1.9.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 (52) hide show
  1. package/Cargo.lock +2 -33
  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/.github/workflows/per-pr.yml +1 -1
  9. package/sdk-core/Cargo.toml +1 -0
  10. package/sdk-core/README.md +1 -1
  11. package/sdk-core/client/src/lib.rs +40 -11
  12. package/sdk-core/client/src/workflow_handle/mod.rs +4 -0
  13. package/sdk-core/core/Cargo.toml +3 -2
  14. package/sdk-core/core/src/core_tests/activity_tasks.rs +69 -2
  15. package/sdk-core/core/src/core_tests/local_activities.rs +99 -4
  16. package/sdk-core/core/src/core_tests/queries.rs +90 -1
  17. package/sdk-core/core/src/core_tests/workflow_tasks.rs +8 -11
  18. package/sdk-core/core/src/telemetry/metrics.rs +4 -4
  19. package/sdk-core/core/src/telemetry/mod.rs +1 -3
  20. package/sdk-core/core/src/test_help/mod.rs +9 -0
  21. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +1 -2
  22. package/sdk-core/core/src/worker/activities/local_activities.rs +1 -1
  23. package/sdk-core/core/src/worker/activities.rs +11 -4
  24. package/sdk-core/core/src/worker/mod.rs +6 -1
  25. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +0 -1
  26. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +28 -6
  27. package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +15 -0
  28. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +19 -15
  29. package/sdk-core/core/src/worker/workflow/mod.rs +89 -59
  30. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +1 -1
  31. package/sdk-core/core-api/Cargo.toml +2 -2
  32. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
  33. package/sdk-core/sdk/Cargo.toml +2 -2
  34. package/sdk-core/sdk/src/lib.rs +13 -8
  35. package/sdk-core/sdk/src/workflow_context.rs +2 -2
  36. package/sdk-core/sdk/src/workflow_future.rs +1 -1
  37. package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
  38. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -0
  39. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +16 -3
  40. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/update.proto +11 -0
  41. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +10 -0
  42. package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +4 -1
  43. package/sdk-core/sdk-core-protos/protos/testsrv_upstream/Makefile +3 -10
  44. package/sdk-core/sdk-core-protos/protos/testsrv_upstream/api-linter.yaml +0 -5
  45. package/sdk-core/sdk-core-protos/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +3 -4
  46. package/sdk-core/sdk-core-protos/src/history_info.rs +2 -2
  47. package/sdk-core/sdk-core-protos/src/lib.rs +1 -0
  48. package/sdk-core/tests/integ_tests/queries_tests.rs +12 -12
  49. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +48 -0
  50. package/src/conversions.rs +19 -17
  51. package/src/runtime.rs +32 -4
  52. package/sdk-core/sdk-core-protos/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +0 -141
package/Cargo.lock CHANGED
@@ -331,19 +331,6 @@ dependencies = [
331
331
  "cfg-if",
332
332
  ]
333
333
 
334
- [[package]]
335
- name = "crossbeam"
336
- version = "0.8.4"
337
- source = "registry+https://github.com/rust-lang/crates.io-index"
338
- checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
339
- dependencies = [
340
- "crossbeam-channel",
341
- "crossbeam-deque",
342
- "crossbeam-epoch",
343
- "crossbeam-queue",
344
- "crossbeam-utils",
345
- ]
346
-
347
334
  [[package]]
348
335
  name = "crossbeam-channel"
349
336
  version = "0.5.11"
@@ -353,25 +340,6 @@ dependencies = [
353
340
  "crossbeam-utils",
354
341
  ]
355
342
 
356
- [[package]]
357
- name = "crossbeam-deque"
358
- version = "0.8.5"
359
- source = "registry+https://github.com/rust-lang/crates.io-index"
360
- checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
361
- dependencies = [
362
- "crossbeam-epoch",
363
- "crossbeam-utils",
364
- ]
365
-
366
- [[package]]
367
- name = "crossbeam-epoch"
368
- version = "0.9.18"
369
- source = "registry+https://github.com/rust-lang/crates.io-index"
370
- checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
371
- dependencies = [
372
- "crossbeam-utils",
373
- ]
374
-
375
343
  [[package]]
376
344
  name = "crossbeam-queue"
377
345
  version = "0.3.11"
@@ -2382,7 +2350,8 @@ dependencies = [
2382
2350
  "arc-swap",
2383
2351
  "async-trait",
2384
2352
  "base64",
2385
- "crossbeam",
2353
+ "crossbeam-channel",
2354
+ "crossbeam-queue",
2386
2355
  "dashmap",
2387
2356
  "derive_builder",
2388
2357
  "derive_more",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@temporalio/core-bridge",
3
- "version": "1.9.0",
3
+ "version": "1.9.2",
4
4
  "description": "Temporal.io SDK Core<>Node bridge",
5
5
  "main": "index.js",
6
6
  "types": "lib/index.d.ts",
@@ -22,7 +22,7 @@
22
22
  "author": "Temporal Technologies Inc. <sdk@temporal.io>",
23
23
  "license": "MIT",
24
24
  "dependencies": {
25
- "@temporalio/common": "1.9.0",
25
+ "@temporalio/common": "^1.9.2",
26
26
  "arg": "^5.0.2",
27
27
  "cargo-cp-artifact": "^0.1.8",
28
28
  "which": "^4.0.0"
@@ -52,5 +52,5 @@
52
52
  "publishConfig": {
53
53
  "access": "public"
54
54
  },
55
- "gitHead": "5096976287616207edcd3e4281a2a5e1f7393e33"
55
+ "gitHead": "5fd66f5787deece0b30f085808db7651187600b1"
56
56
  }
@@ -65,7 +65,7 @@ jobs:
65
65
  - uses: actions-rs/cargo@v1
66
66
  with:
67
67
  command: test
68
- args: -- --include-ignored
68
+ args: -- --include-ignored --nocapture
69
69
  - uses: actions/upload-artifact@v3
70
70
  with:
71
71
  name: coverage-report
@@ -7,6 +7,7 @@ license = "MIT"
7
7
  license-file = "LICENSE.txt"
8
8
 
9
9
  [workspace.dependencies]
10
+ derive_more = { version = "0.99", default-features = false, features = ["constructor", "display", "from", "into"] }
10
11
  tonic = "0.9"
11
12
  tonic-build = "0.9"
12
13
  opentelemetry = "0.21"
@@ -89,7 +89,7 @@ equivalent.
89
89
  This repo uses a subtree for upstream protobuf files. The path `sdk-core-protos/protos/api_upstream` is a
90
90
  subtree. To update it, use:
91
91
 
92
- `git pull --squash -s subtree ssh://git@github.com/temporalio/api.git master --allow-unrelated-histories`
92
+ `git pull --squash --rebase=false -s subtree ssh://git@github.com/temporalio/api.git master --allow-unrelated-histories`
93
93
 
94
94
  Do not question why this git command is the way it is. It is not our place to interpret git's ways.
95
95
 
@@ -6,6 +6,7 @@
6
6
 
7
7
  #[macro_use]
8
8
  extern crate tracing;
9
+
9
10
  mod metrics;
10
11
  mod raw;
11
12
  mod retry;
@@ -49,7 +50,7 @@ use temporal_sdk_core_protos::{
49
50
  coresdk::{workflow_commands::QueryResult, IntoPayloadsExt},
50
51
  grpc::health::v1::health_client::HealthClient,
51
52
  temporal::api::{
52
- common::v1::{Header, Payload, Payloads, WorkflowExecution, WorkflowType},
53
+ common::v1::{Header, Payload, Payloads, RetryPolicy, WorkflowExecution, WorkflowType},
53
54
  enums::v1::{TaskQueueKind, WorkflowIdReusePolicy},
54
55
  failure::v1::Failure,
55
56
  operatorservice::v1::operator_service_client::OperatorServiceClient,
@@ -151,9 +152,9 @@ pub struct TlsConfig {
151
152
  /// If using mTLS, both the client cert and private key must be specified, this contains them.
152
153
  #[derive(Clone)]
153
154
  pub struct ClientTlsConfig {
154
- /// The certificate for this client
155
+ /// The certificate for this client, encoded as PEM
155
156
  pub client_cert: Vec<u8>,
156
- /// The private key for this client
157
+ /// The private key for this client, encoded as PEM
157
158
  pub client_private_key: Vec<u8>,
158
159
  }
159
160
 
@@ -316,6 +317,7 @@ impl<C> Deref for ConfiguredClient<C> {
316
317
  &self.client
317
318
  }
318
319
  }
320
+
319
321
  impl<C> DerefMut for ConfiguredClient<C> {
320
322
  fn deref_mut(&mut self) -> &mut Self::Target {
321
323
  &mut self.client
@@ -493,6 +495,19 @@ pub struct TemporalServiceClient<T> {
493
495
  test_svc_client: OnceCell<TestServiceClient<T>>,
494
496
  health_svc_client: OnceCell<HealthClient<T>>,
495
497
  }
498
+
499
+ /// We up the limit on incoming messages from server from the 4Mb default to 128Mb. If for
500
+ /// whatever reason this needs to be changed by the user, we support overriding it via env var.
501
+ fn get_decode_max_size() -> usize {
502
+ static _DECODE_MAX_SIZE: OnceCell<usize> = OnceCell::new();
503
+ *_DECODE_MAX_SIZE.get_or_init(|| {
504
+ std::env::var("TEMPORAL_MAX_INCOMING_GRPC_BYTES")
505
+ .ok()
506
+ .and_then(|s| s.parse().ok())
507
+ .unwrap_or(128 * 1024 * 1024)
508
+ })
509
+ }
510
+
496
511
  impl<T> TemporalServiceClient<T>
497
512
  where
498
513
  T: Clone,
@@ -512,23 +527,30 @@ where
512
527
  }
513
528
  /// Get the underlying workflow service client
514
529
  pub fn workflow_svc(&self) -> &WorkflowServiceClient<T> {
515
- self.workflow_svc_client
516
- .get_or_init(|| WorkflowServiceClient::new(self.svc.clone()))
530
+ self.workflow_svc_client.get_or_init(|| {
531
+ WorkflowServiceClient::new(self.svc.clone())
532
+ .max_decoding_message_size(get_decode_max_size())
533
+ })
517
534
  }
518
535
  /// Get the underlying operator service client
519
536
  pub fn operator_svc(&self) -> &OperatorServiceClient<T> {
520
- self.operator_svc_client
521
- .get_or_init(|| OperatorServiceClient::new(self.svc.clone()))
537
+ self.operator_svc_client.get_or_init(|| {
538
+ OperatorServiceClient::new(self.svc.clone())
539
+ .max_decoding_message_size(get_decode_max_size())
540
+ })
522
541
  }
523
542
  /// Get the underlying test service client
524
543
  pub fn test_svc(&self) -> &TestServiceClient<T> {
525
- self.test_svc_client
526
- .get_or_init(|| TestServiceClient::new(self.svc.clone()))
544
+ self.test_svc_client.get_or_init(|| {
545
+ TestServiceClient::new(self.svc.clone())
546
+ .max_decoding_message_size(get_decode_max_size())
547
+ })
527
548
  }
528
549
  /// Get the underlying health service client
529
550
  pub fn health_svc(&self) -> &HealthClient<T> {
530
- self.health_svc_client
531
- .get_or_init(|| HealthClient::new(self.svc.clone()))
551
+ self.health_svc_client.get_or_init(|| {
552
+ HealthClient::new(self.svc.clone()).max_decoding_message_size(get_decode_max_size())
553
+ })
532
554
  }
533
555
  /// Get the underlying workflow service client mutably
534
556
  pub fn workflow_svc_mut(&mut self) -> &mut WorkflowServiceClient<T> {
@@ -551,6 +573,7 @@ where
551
573
  self.health_svc_client.get_mut().unwrap()
552
574
  }
553
575
  }
576
+
554
577
  /// A [WorkflowServiceClient] with the default interceptors attached.
555
578
  pub type WorkflowServiceClientWithMetrics = WorkflowServiceClient<InterceptedMetricsSvc>;
556
579
  /// An [OperatorServiceClient] with the default interceptors attached.
@@ -986,6 +1009,9 @@ pub struct WorkflowOptions {
986
1009
  /// Optionally enable Eager Workflow Start, a latency optimization using local workers
987
1010
  /// NOTE: Experimental and incompatible with versioning with BuildIDs
988
1011
  pub enable_eager_workflow_start: bool,
1012
+
1013
+ /// Optionally set a retry policy for the workflow
1014
+ pub retry_policy: Option<RetryPolicy>,
989
1015
  }
990
1016
 
991
1017
  #[async_trait::async_trait]
@@ -1023,6 +1049,7 @@ impl WorkflowClientTrait for Client {
1023
1049
  search_attributes: options.search_attributes.map(|d| d.into()),
1024
1050
  cron_schedule: options.cron_schedule.unwrap_or_default(),
1025
1051
  request_eager_execution: options.enable_eager_workflow_start,
1052
+ retry_policy: options.retry_policy,
1026
1053
  ..Default::default()
1027
1054
  },
1028
1055
  )
@@ -1497,6 +1524,7 @@ mod sealed {
1497
1524
  WorkflowClientTrait + RawClientLike<SvcType = InterceptedMetricsSvc>
1498
1525
  {
1499
1526
  }
1527
+
1500
1528
  impl<T> WfHandleClient for T where
1501
1529
  T: WorkflowClientTrait + RawClientLike<SvcType = InterceptedMetricsSvc>
1502
1530
  {
@@ -1523,6 +1551,7 @@ pub trait WfClientExt: WfHandleClient + Sized + Clone {
1523
1551
  )
1524
1552
  }
1525
1553
  }
1554
+
1526
1555
  impl<T> WfClientExt for T where T: WfHandleClient + Clone + Sized {}
1527
1556
 
1528
1557
  #[cfg(test)]
@@ -91,6 +91,10 @@ where
91
91
  }
92
92
  }
93
93
 
94
+ pub fn info(&self) -> &WorkflowExecutionInfo {
95
+ &self.info
96
+ }
97
+
94
98
  pub async fn get_workflow_result(
95
99
  &self,
96
100
  opts: GetWorkflowResultOpts,
@@ -27,10 +27,11 @@ arc-swap = "1.3"
27
27
  async-trait = "0.1"
28
28
  base64 = "0.21"
29
29
  console-subscriber = { version = "0.1", optional = true }
30
- crossbeam = "0.8"
30
+ crossbeam-channel = "0.5"
31
+ crossbeam-queue = "0.3"
31
32
  dashmap = "5.5"
32
33
  derive_builder = "0.12"
33
- derive_more = "0.99"
34
+ derive_more = { workspace = true }
34
35
  enum_dispatch = "0.3"
35
36
  enum-iterator = "1.4"
36
37
  flate2 = { version = "1.0", optional = true }
@@ -17,7 +17,7 @@ use std::{
17
17
  future,
18
18
  rc::Rc,
19
19
  sync::{
20
- atomic::{AtomicUsize, Ordering},
20
+ atomic::{AtomicBool, AtomicUsize, Ordering},
21
21
  Arc,
22
22
  },
23
23
  time::Duration,
@@ -1057,7 +1057,6 @@ async fn cant_complete_activity_with_unset_result_payload() {
1057
1057
  #[rstest::rstest]
1058
1058
  #[tokio::test]
1059
1059
  async fn graceful_shutdown(#[values(true, false)] at_max_outstanding: bool) {
1060
- let _task_q = "q";
1061
1060
  let grace_period = Duration::from_millis(200);
1062
1061
  let mut tasks = three_tasks();
1063
1062
  let mut mock_act_poller = mock_poller();
@@ -1122,3 +1121,71 @@ async fn graceful_shutdown(#[values(true, false)] at_max_outstanding: bool) {
1122
1121
  }
1123
1122
  worker.drain_pollers_and_shutdown().await;
1124
1123
  }
1124
+
1125
+ #[rstest::rstest]
1126
+ #[tokio::test]
1127
+ async fn activities_must_be_flushed_to_server_on_shutdown(#[values(true, false)] use_grace: bool) {
1128
+ let grace_period = if use_grace {
1129
+ // Even though the grace period is shorter than the client call, the client call will still
1130
+ // go through. This is reasonable since the client has a timeout anyway, and it's unlikely
1131
+ // that a user *needs* an extremely short grace period (it'd be kind of pointless in that
1132
+ // case). They can always force-kill their worker in this situation.
1133
+ Duration::from_millis(50)
1134
+ } else {
1135
+ Duration::from_secs(10)
1136
+ };
1137
+ let shutdown_finished: &'static AtomicBool = Box::leak(Box::new(AtomicBool::new(false)));
1138
+ let mut tasks = three_tasks();
1139
+ let mut mock_act_poller = mock_poller();
1140
+ mock_act_poller
1141
+ .expect_poll()
1142
+ .times(1)
1143
+ .returning(move || Some(Ok(tasks.pop_front().unwrap())));
1144
+ mock_act_poller
1145
+ .expect_poll()
1146
+ .times(1)
1147
+ .returning(move || None);
1148
+ let mut mock_client = mock_manual_workflow_client();
1149
+ mock_client
1150
+ .expect_complete_activity_task()
1151
+ .times(1)
1152
+ .returning(|_, _| {
1153
+ async {
1154
+ // We need some artificial delay here and there's nothing meaningful to sync with
1155
+ tokio::time::sleep(Duration::from_millis(100)).await;
1156
+ if shutdown_finished.load(Ordering::Acquire) {
1157
+ panic!("Shutdown must complete *after* server sees the activity completion");
1158
+ }
1159
+ Ok(Default::default())
1160
+ }
1161
+ .boxed()
1162
+ });
1163
+
1164
+ let mw = MockWorkerInputs {
1165
+ act_poller: Some(Box::from(mock_act_poller)),
1166
+ config: test_worker_cfg()
1167
+ .graceful_shutdown_period(grace_period)
1168
+ .max_concurrent_at_polls(1_usize) // Makes test logic simple
1169
+ .build()
1170
+ .unwrap(),
1171
+ ..Default::default()
1172
+ };
1173
+ let worker = mock_worker(MocksHolder::from_mock_worker(mock_client, mw));
1174
+
1175
+ let task = worker.poll_activity_task().await.unwrap();
1176
+
1177
+ let shutdown_task = async {
1178
+ worker.drain_activity_poller_and_shutdown().await;
1179
+ shutdown_finished.store(true, Ordering::Release);
1180
+ };
1181
+ let complete_task = async {
1182
+ worker
1183
+ .complete_activity_task(ActivityTaskCompletion {
1184
+ task_token: task.task_token,
1185
+ result: Some(ActivityExecutionResult::ok("hi".into())),
1186
+ })
1187
+ .await
1188
+ .unwrap();
1189
+ };
1190
+ join!(shutdown_task, complete_task);
1191
+ }
@@ -2,13 +2,13 @@ use crate::{
2
2
  prost_dur,
3
3
  replay::{default_wes_attribs, TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE},
4
4
  test_help::{
5
- hist_to_poll_resp, mock_sdk, mock_sdk_cfg, mock_worker, single_hist_mock_sg, MockPollCfg,
6
- ResponseType, WorkerExt,
5
+ build_mock_pollers, hist_to_poll_resp, mock_sdk, mock_sdk_cfg, mock_worker,
6
+ single_hist_mock_sg, MockPollCfg, ResponseType, WorkerExt,
7
7
  },
8
8
  worker::{client::mocks::mock_workflow_client, LEGACY_QUERY_ID},
9
9
  };
10
10
  use anyhow::anyhow;
11
- use crossbeam::queue::SegQueue;
11
+ use crossbeam_queue::SegQueue;
12
12
  use futures::{future::join_all, FutureExt};
13
13
  use std::{
14
14
  collections::HashMap,
@@ -38,7 +38,7 @@ use temporal_sdk_core_protos::{
38
38
  },
39
39
  temporal::api::{
40
40
  common::v1::RetryPolicy,
41
- enums::v1::{EventType, TimeoutType, WorkflowTaskFailedCause},
41
+ enums::v1::{CommandType, EventType, TimeoutType, WorkflowTaskFailedCause},
42
42
  failure::v1::{failure::FailureInfo, Failure},
43
43
  query::v1::WorkflowQuery,
44
44
  },
@@ -1295,3 +1295,98 @@ async fn queries_can_be_received_while_heartbeating() {
1295
1295
 
1296
1296
  core.drain_pollers_and_shutdown().await;
1297
1297
  }
1298
+
1299
+ #[tokio::test]
1300
+ async fn local_activity_after_wf_complete_is_discarded() {
1301
+ let wfid = "fake_wf_id";
1302
+ let mut t = TestHistoryBuilder::default();
1303
+ t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
1304
+ t.add_full_wf_task();
1305
+ t.add_workflow_task_scheduled_and_started();
1306
+
1307
+ let mock = mock_workflow_client();
1308
+ let mut mock_cfg = MockPollCfg::from_resp_batches(
1309
+ wfid,
1310
+ t,
1311
+ [ResponseType::ToTaskNum(1), ResponseType::ToTaskNum(2)],
1312
+ mock,
1313
+ );
1314
+ mock_cfg.make_poll_stream_interminable = true;
1315
+ mock_cfg.completion_asserts_from_expectations(|mut asserts| {
1316
+ asserts
1317
+ .then(move |wft| {
1318
+ assert_eq!(wft.commands.len(), 0);
1319
+ })
1320
+ .then(move |wft| {
1321
+ assert_eq!(wft.commands.len(), 2);
1322
+ assert_eq!(wft.commands[0].command_type(), CommandType::RecordMarker);
1323
+ assert_eq!(
1324
+ wft.commands[1].command_type(),
1325
+ CommandType::CompleteWorkflowExecution
1326
+ );
1327
+ });
1328
+ });
1329
+ let mut mock = build_mock_pollers(mock_cfg);
1330
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
1331
+ let core = mock_worker(mock);
1332
+
1333
+ let barr = Barrier::new(2);
1334
+
1335
+ let task = core.poll_workflow_activation().await.unwrap();
1336
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1337
+ task.run_id,
1338
+ vec![
1339
+ ScheduleLocalActivity {
1340
+ seq: 1,
1341
+ activity_id: "1".to_string(),
1342
+ activity_type: "test_act".to_string(),
1343
+ start_to_close_timeout: Some(prost_dur!(from_secs(30))),
1344
+ ..Default::default()
1345
+ }
1346
+ .into(),
1347
+ ScheduleLocalActivity {
1348
+ seq: 2,
1349
+ activity_id: "2".to_string(),
1350
+ activity_type: "test_act".to_string(),
1351
+ start_to_close_timeout: Some(prost_dur!(from_secs(30))),
1352
+ ..Default::default()
1353
+ }
1354
+ .into(),
1355
+ ],
1356
+ ))
1357
+ .await
1358
+ .unwrap();
1359
+
1360
+ let wf_poller = async {
1361
+ let task = core.poll_workflow_activation().await.unwrap();
1362
+ assert_matches!(
1363
+ task.jobs.as_slice(),
1364
+ [WorkflowActivationJob {
1365
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(_)),
1366
+ }]
1367
+ );
1368
+ barr.wait().await;
1369
+ core.complete_execution(&task.run_id).await;
1370
+ };
1371
+
1372
+ let at_poller = async {
1373
+ let act_task = core.poll_activity_task().await.unwrap();
1374
+ core.complete_activity_task(ActivityTaskCompletion {
1375
+ task_token: act_task.task_token,
1376
+ result: Some(ActivityExecutionResult::ok(vec![1].into())),
1377
+ })
1378
+ .await
1379
+ .unwrap();
1380
+ let act_task = core.poll_activity_task().await.unwrap();
1381
+ barr.wait().await;
1382
+ core.complete_activity_task(ActivityTaskCompletion {
1383
+ task_token: act_task.task_token,
1384
+ result: Some(ActivityExecutionResult::ok(vec![2].into())),
1385
+ })
1386
+ .await
1387
+ .unwrap();
1388
+ };
1389
+
1390
+ join!(wf_poller, at_poller);
1391
+ core.drain_pollers_and_shutdown().await;
1392
+ }
@@ -1,10 +1,11 @@
1
1
  use crate::{
2
2
  test_help::{
3
3
  build_mock_pollers, canned_histories, hist_to_poll_resp, mock_worker, single_hist_mock_sg,
4
- MockPollCfg, ResponseType, WorkerExt,
4
+ MockPollCfg, MocksHolder, ResponseType, WorkerExt,
5
5
  },
6
6
  worker::{client::mocks::mock_workflow_client, LEGACY_QUERY_ID},
7
7
  };
8
+ use futures_util::stream;
8
9
  use std::{
9
10
  collections::{HashMap, VecDeque},
10
11
  time::Duration,
@@ -891,3 +892,91 @@ async fn build_id_set_properly_on_query_on_first_task() {
891
892
  .unwrap();
892
893
  core.drain_pollers_and_shutdown().await;
893
894
  }
895
+
896
+ #[tokio::test]
897
+ async fn queries_arent_lost_in_buffer_void() {
898
+ let wfid = "fake_wf_id";
899
+ let mut t = TestHistoryBuilder::default();
900
+ t.add_by_type(EventType::WorkflowExecutionStarted);
901
+ t.add_full_wf_task();
902
+ t.add_we_signaled("sig", vec![]);
903
+ t.add_full_wf_task();
904
+ t.add_workflow_execution_completed();
905
+ let tasks = [
906
+ hist_to_poll_resp(&t, wfid.to_owned(), 1.into()),
907
+ {
908
+ let mut pr = hist_to_poll_resp(&t, wfid.to_owned(), 1.into());
909
+ pr.query = Some(WorkflowQuery {
910
+ query_type: "1".to_string(),
911
+ ..Default::default()
912
+ });
913
+ pr.started_event_id = 0;
914
+ pr
915
+ },
916
+ {
917
+ let mut pr = hist_to_poll_resp(&t, wfid.to_owned(), 1.into());
918
+ pr.query = Some(WorkflowQuery {
919
+ query_type: "2".to_string(),
920
+ ..Default::default()
921
+ });
922
+ pr.started_event_id = 0;
923
+ pr
924
+ },
925
+ hist_to_poll_resp(&t, wfid.to_owned(), 2.into()),
926
+ ]
927
+ .map(|r| r.resp);
928
+
929
+ let mut mock = mock_workflow_client();
930
+ mock.expect_complete_workflow_task()
931
+ .returning(|_| Ok(Default::default()));
932
+ mock.expect_respond_legacy_query()
933
+ .times(2)
934
+ .returning(|_, _| Ok(Default::default()));
935
+ let mut mock = MocksHolder::from_wft_stream(mock, stream::iter(tasks));
936
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 1);
937
+ let core = mock_worker(mock);
938
+
939
+ let task = core.poll_workflow_activation().await.unwrap();
940
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(task.run_id))
941
+ .await
942
+ .unwrap();
943
+
944
+ let task = core.poll_workflow_activation().await.unwrap();
945
+ assert_matches!(
946
+ task.jobs.as_slice(),
947
+ [WorkflowActivationJob {
948
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(q)),
949
+ }] => q.query_type == "1"
950
+ );
951
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
952
+ task.run_id,
953
+ query_ok(LEGACY_QUERY_ID.to_string(), "hi"),
954
+ ))
955
+ .await
956
+ .unwrap();
957
+
958
+ let task = core.poll_workflow_activation().await.unwrap();
959
+ assert_matches!(
960
+ task.jobs.as_slice(),
961
+ [WorkflowActivationJob {
962
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(q)),
963
+ }] => q.query_type == "2"
964
+ );
965
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
966
+ task.run_id,
967
+ query_ok(LEGACY_QUERY_ID.to_string(), "hi"),
968
+ ))
969
+ .await
970
+ .unwrap();
971
+
972
+ let task = core.poll_workflow_activation().await.unwrap();
973
+ assert_matches!(
974
+ task.jobs.as_slice(),
975
+ [WorkflowActivationJob {
976
+ variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
977
+ }]
978
+ );
979
+ core.complete_execution(&task.run_id).await;
980
+
981
+ core.shutdown().await;
982
+ }
@@ -2112,15 +2112,12 @@ async fn continue_as_new_preserves_some_values() {
2112
2112
  wes_attrs.search_attributes = Some(search);
2113
2113
  wes_attrs.retry_policy = Some(retry_policy);
2114
2114
  let mut mock_client = mock_workflow_client();
2115
- let hist = {
2115
+ let t = {
2116
2116
  let mut t = TestHistoryBuilder::default();
2117
2117
  t.add(wes_attrs.clone());
2118
2118
  t.add_full_wf_task();
2119
2119
  t
2120
2120
  };
2121
- mock_client.expect_poll_workflow_task().returning(move |_| {
2122
- Ok(hist_to_poll_resp(&hist, wfid.to_owned(), ResponseType::AllHistory).resp)
2123
- });
2124
2121
  mock_client
2125
2122
  .expect_complete_workflow_task()
2126
2123
  .returning(move |mut c| {
@@ -2135,8 +2132,8 @@ async fn continue_as_new_preserves_some_values() {
2135
2132
  }
2136
2133
  Ok(Default::default())
2137
2134
  });
2138
-
2139
- let worker = Worker::new_test(test_worker_cfg().build().unwrap(), mock_client);
2135
+ let mock = single_hist_mock_sg(wfid, t, vec![ResponseType::AllHistory], mock_client, true);
2136
+ let worker = mock_worker(mock);
2140
2137
  let r = worker.poll_workflow_activation().await.unwrap();
2141
2138
  worker
2142
2139
  .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
@@ -2149,6 +2146,7 @@ async fn continue_as_new_preserves_some_values() {
2149
2146
  ))
2150
2147
  .await
2151
2148
  .unwrap();
2149
+ worker.shutdown().await;
2152
2150
  }
2153
2151
 
2154
2152
  #[rstest]
@@ -2737,15 +2735,12 @@ async fn use_compatible_version_flag(
2737
2735
  ) {
2738
2736
  let wfid = "fake_wf_id";
2739
2737
  let mut mock_client = mock_workflow_client();
2740
- let hist = {
2738
+ let t = {
2741
2739
  let mut t = TestHistoryBuilder::default();
2742
2740
  t.add_by_type(EventType::WorkflowExecutionStarted);
2743
2741
  t.add_full_wf_task();
2744
2742
  t
2745
2743
  };
2746
- mock_client.expect_poll_workflow_task().returning(move |_| {
2747
- Ok(hist_to_poll_resp(&hist, wfid.to_owned(), ResponseType::AllHistory).resp)
2748
- });
2749
2744
  let compat_flag_expected = match intent {
2750
2745
  VersioningIntent::Unspecified => !different_tq,
2751
2746
  VersioningIntent::Compatible => true,
@@ -2770,7 +2765,8 @@ async fn use_compatible_version_flag(
2770
2765
  Ok(Default::default())
2771
2766
  });
2772
2767
 
2773
- let worker = Worker::new_test(test_worker_cfg().build().unwrap(), mock_client);
2768
+ let mock = single_hist_mock_sg(wfid, t, vec![ResponseType::AllHistory], mock_client, true);
2769
+ let worker = mock_worker(mock);
2774
2770
  let r = worker.poll_workflow_activation().await.unwrap();
2775
2771
  let task_queue = if different_tq {
2776
2772
  "enchi cat!".to_string()
@@ -2806,6 +2802,7 @@ async fn use_compatible_version_flag(
2806
2802
  .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(r.run_id, cmd))
2807
2803
  .await
2808
2804
  .unwrap();
2805
+ worker.shutdown().await;
2809
2806
  }
2810
2807
 
2811
2808
  #[tokio::test]