@temporalio/core-bridge 0.19.2 → 0.20.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 (125) hide show
  1. package/Cargo.lock +90 -157
  2. package/Cargo.toml +1 -0
  3. package/index.d.ts +11 -27
  4. package/package.json +3 -3
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.cargo/config.toml +1 -0
  13. package/sdk-core/CODEOWNERS +1 -1
  14. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +119 -86
  15. package/sdk-core/bridge-ffi/src/lib.rs +311 -315
  16. package/sdk-core/bridge-ffi/src/wrappers.rs +108 -113
  17. package/sdk-core/client/Cargo.toml +13 -9
  18. package/sdk-core/client/LICENSE.txt +23 -0
  19. package/sdk-core/client/src/lib.rs +286 -174
  20. package/sdk-core/client/src/metrics.rs +86 -12
  21. package/sdk-core/client/src/raw.rs +566 -0
  22. package/sdk-core/client/src/retry.rs +137 -99
  23. package/sdk-core/core/Cargo.toml +15 -10
  24. package/sdk-core/core/LICENSE.txt +23 -0
  25. package/sdk-core/core/benches/workflow_replay.rs +79 -0
  26. package/sdk-core/core/src/abstractions.rs +38 -0
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +108 -182
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +16 -11
  29. package/sdk-core/core/src/core_tests/determinism.rs +24 -12
  30. package/sdk-core/core/src/core_tests/local_activities.rs +53 -27
  31. package/sdk-core/core/src/core_tests/mod.rs +30 -43
  32. package/sdk-core/core/src/core_tests/queries.rs +82 -81
  33. package/sdk-core/core/src/core_tests/workers.rs +111 -296
  34. package/sdk-core/core/src/core_tests/workflow_cancels.rs +4 -4
  35. package/sdk-core/core/src/core_tests/workflow_tasks.rs +257 -242
  36. package/sdk-core/core/src/lib.rs +73 -318
  37. package/sdk-core/core/src/pollers/mod.rs +4 -6
  38. package/sdk-core/core/src/pollers/poll_buffer.rs +20 -14
  39. package/sdk-core/core/src/protosext/mod.rs +7 -10
  40. package/sdk-core/core/src/replay/mod.rs +11 -150
  41. package/sdk-core/core/src/telemetry/metrics.rs +35 -2
  42. package/sdk-core/core/src/telemetry/mod.rs +49 -16
  43. package/sdk-core/core/src/telemetry/prometheus_server.rs +14 -35
  44. package/sdk-core/core/src/test_help/mod.rs +104 -170
  45. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +57 -34
  46. package/sdk-core/core/src/worker/activities/local_activities.rs +95 -23
  47. package/sdk-core/core/src/worker/activities.rs +23 -16
  48. package/sdk-core/core/src/worker/client/mocks.rs +86 -0
  49. package/sdk-core/core/src/worker/client.rs +209 -0
  50. package/sdk-core/core/src/worker/mod.rs +207 -108
  51. package/sdk-core/core/src/workflow/driven_workflow.rs +21 -6
  52. package/sdk-core/core/src/workflow/history_update.rs +107 -24
  53. package/sdk-core/core/src/workflow/machines/activity_state_machine.rs +2 -3
  54. package/sdk-core/core/src/workflow/machines/child_workflow_state_machine.rs +2 -3
  55. package/sdk-core/core/src/workflow/machines/mod.rs +20 -17
  56. package/sdk-core/core/src/workflow/machines/signal_external_state_machine.rs +56 -19
  57. package/sdk-core/core/src/workflow/machines/transition_coverage.rs +5 -0
  58. package/sdk-core/core/src/workflow/machines/upsert_search_attributes_state_machine.rs +230 -22
  59. package/sdk-core/core/src/workflow/machines/workflow_machines.rs +81 -115
  60. package/sdk-core/core/src/workflow/machines/workflow_task_state_machine.rs +4 -4
  61. package/sdk-core/core/src/workflow/mod.rs +13 -1
  62. package/sdk-core/core/src/workflow/workflow_tasks/concurrency_manager.rs +70 -11
  63. package/sdk-core/core/src/workflow/workflow_tasks/mod.rs +65 -41
  64. package/sdk-core/core-api/Cargo.toml +9 -1
  65. package/sdk-core/core-api/LICENSE.txt +23 -0
  66. package/sdk-core/core-api/src/errors.rs +7 -38
  67. package/sdk-core/core-api/src/lib.rs +44 -52
  68. package/sdk-core/core-api/src/worker.rs +10 -2
  69. package/sdk-core/etc/deps.svg +127 -96
  70. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +11 -7
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +10 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +6 -1
  73. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +6 -0
  74. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +6 -0
  75. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +2 -1
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +3 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +12 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +25 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -0
  80. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +19 -35
  81. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +2 -6
  82. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +53 -11
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +14 -7
  84. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +3 -5
  85. package/sdk-core/sdk/Cargo.toml +16 -2
  86. package/sdk-core/sdk/LICENSE.txt +23 -0
  87. package/sdk-core/sdk/src/interceptors.rs +11 -0
  88. package/sdk-core/sdk/src/lib.rs +139 -151
  89. package/sdk-core/sdk/src/workflow_context/options.rs +86 -1
  90. package/sdk-core/sdk/src/workflow_context.rs +36 -17
  91. package/sdk-core/sdk/src/workflow_future.rs +19 -25
  92. package/sdk-core/sdk-core-protos/Cargo.toml +1 -1
  93. package/sdk-core/sdk-core-protos/build.rs +1 -0
  94. package/sdk-core/sdk-core-protos/src/history_info.rs +17 -4
  95. package/sdk-core/sdk-core-protos/src/lib.rs +251 -47
  96. package/sdk-core/test-utils/Cargo.toml +3 -1
  97. package/sdk-core/test-utils/src/canned_histories.rs +27 -0
  98. package/sdk-core/test-utils/src/histfetch.rs +3 -3
  99. package/sdk-core/test-utils/src/lib.rs +223 -68
  100. package/sdk-core/tests/integ_tests/client_tests.rs +27 -4
  101. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +93 -14
  102. package/sdk-core/tests/integ_tests/polling_tests.rs +18 -12
  103. package/sdk-core/tests/integ_tests/queries_tests.rs +50 -53
  104. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +117 -103
  105. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +8 -1
  106. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +10 -5
  107. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +7 -1
  108. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +32 -9
  109. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +7 -1
  110. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +76 -15
  111. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +19 -3
  112. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +39 -42
  113. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +84 -0
  114. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +30 -8
  115. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +21 -6
  116. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +26 -16
  117. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +66 -0
  118. package/sdk-core/tests/integ_tests/workflow_tests.rs +78 -74
  119. package/sdk-core/tests/load_tests.rs +9 -6
  120. package/sdk-core/tests/main.rs +43 -10
  121. package/src/conversions.rs +7 -12
  122. package/src/lib.rs +322 -357
  123. package/sdk-core/client/src/mocks.rs +0 -167
  124. package/sdk-core/core/src/worker/dispatcher.rs +0 -171
  125. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +0 -61
@@ -0,0 +1,38 @@
1
+ //! This module contains very generic helpers that can be used codebase-wide
2
+
3
+ use crate::MetricsContext;
4
+ use tokio::sync::{AcquireError, Semaphore, SemaphorePermit};
5
+
6
+ /// Wraps a [Semaphore] with a function call that is fed the available permits any time a permit is
7
+ /// acquired or restored through the provided methods
8
+ pub(crate) struct MeteredSemaphore {
9
+ pub sem: Semaphore,
10
+ metrics_ctx: MetricsContext,
11
+ record_fn: fn(&MetricsContext, usize),
12
+ }
13
+
14
+ impl MeteredSemaphore {
15
+ pub fn new(
16
+ inital_permits: usize,
17
+ metrics_ctx: MetricsContext,
18
+ record_fn: fn(&MetricsContext, usize),
19
+ ) -> Self {
20
+ Self {
21
+ sem: Semaphore::new(inital_permits),
22
+ metrics_ctx,
23
+ record_fn,
24
+ }
25
+ }
26
+
27
+ pub async fn acquire(&self) -> Result<SemaphorePermit<'_>, AcquireError> {
28
+ let res = self.sem.acquire().await;
29
+ (self.record_fn)(&self.metrics_ctx, self.sem.available_permits());
30
+ res
31
+ }
32
+
33
+ /// Adds just one permit
34
+ pub fn add_permit(&self) {
35
+ self.sem.add_permits(1);
36
+ (self.record_fn)(&self.metrics_ctx, self.sem.available_permits());
37
+ }
38
+ }
@@ -1,29 +1,26 @@
1
1
  use crate::{
2
2
  job_assert,
3
3
  test_help::{
4
- build_fake_core, canned_histories, gen_assert_and_reply, mock_core,
5
- mock_core_with_opts_no_workers, mock_manual_poller, mock_poller, mock_poller_from_resps,
6
- poll_and_reply, MockWorker, MocksHolder, TEST_Q,
4
+ build_fake_worker, canned_histories, gen_assert_and_reply, mock_manual_poller, mock_poller,
5
+ mock_worker, poll_and_reply, test_worker_cfg, MockWorker, MocksHolder,
7
6
  },
7
+ worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
8
8
  workflow::WorkflowCachingPolicy::NonSticky,
9
- ActivityHeartbeat, ActivityTask, Core, CoreInitOptionsBuilder, CoreSDK, WorkerConfigBuilder,
9
+ ActivityHeartbeat, Worker, WorkerConfigBuilder,
10
10
  };
11
11
  use futures::FutureExt;
12
12
  use std::{
13
13
  cell::RefCell,
14
14
  collections::{hash_map::Entry, HashMap, VecDeque},
15
15
  rc::Rc,
16
- sync::{
17
- atomic::{AtomicUsize, Ordering},
18
- Arc,
19
- },
16
+ sync::atomic::{AtomicUsize, Ordering},
20
17
  time::Duration,
21
18
  };
22
- use temporal_client::mocks::{fake_sg_opts, mock_gateway, mock_manual_gateway};
19
+ use temporal_sdk_core_api::Worker as WorkerTrait;
23
20
  use temporal_sdk_core_protos::{
24
21
  coresdk::{
25
22
  activity_result::{activity_resolution, ActivityExecutionResult, ActivityResolution},
26
- activity_task::activity_task,
23
+ activity_task::{activity_task, ActivityTask},
27
24
  workflow_activation::{workflow_activation_job, ResolveActivity, WorkflowActivationJob},
28
25
  workflow_commands::{
29
26
  ActivityCancellationType, CompleteWorkflowExecution, RequestCancelActivity,
@@ -38,11 +35,11 @@ use temporal_sdk_core_protos::{
38
35
  },
39
36
  };
40
37
  use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd};
41
- use tokio::{join, sync::Notify, time::sleep};
38
+ use tokio::{join, time::sleep};
42
39
 
43
40
  #[tokio::test]
44
41
  async fn max_activities_respected() {
45
- let task_q = "q";
42
+ let _task_q = "q";
46
43
  let mut tasks = VecDeque::from(vec![
47
44
  PollActivityTaskQueueResponse {
48
45
  task_token: vec![1],
@@ -60,47 +57,38 @@ async fn max_activities_respected() {
60
57
  ..Default::default()
61
58
  },
62
59
  ]);
63
- let mut mock_gateway = mock_gateway();
64
- mock_gateway
60
+ let mut mock_client = mock_workflow_client();
61
+ mock_client
65
62
  .expect_poll_activity_task()
66
63
  .times(3)
67
- .returning(move |_| Ok(tasks.pop_front().unwrap()));
68
- mock_gateway
64
+ .returning(move |_, _| Ok(tasks.pop_front().unwrap()));
65
+ mock_client
69
66
  .expect_complete_activity_task()
70
67
  .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
71
68
 
72
- let core = CoreSDK::new(
73
- mock_gateway,
74
- CoreInitOptionsBuilder::default()
75
- .gateway_opts(fake_sg_opts())
76
- .build()
77
- .unwrap(),
78
- );
79
- core.register_worker(
80
- WorkerConfigBuilder::default()
81
- .task_queue(TEST_Q)
69
+ let worker = Worker::new_test(
70
+ test_worker_cfg()
82
71
  .max_outstanding_activities(2_usize)
83
72
  .build()
84
73
  .unwrap(),
85
- )
86
- .unwrap();
74
+ mock_client,
75
+ );
87
76
 
88
77
  // We allow two outstanding activities, therefore first two polls should return right away
89
- let r1 = core.poll_activity_task(task_q).await.unwrap();
90
- let _r2 = core.poll_activity_task(task_q).await.unwrap();
78
+ let r1 = worker.poll_activity_task().await.unwrap();
79
+ let _r2 = worker.poll_activity_task().await.unwrap();
91
80
  // Third should block until we complete one of the first two
92
81
  let last_finisher = AtomicUsize::new(0);
93
82
  tokio::join! {
94
83
  async {
95
- core.complete_activity_task(ActivityTaskCompletion {
84
+ worker.complete_activity_task(ActivityTaskCompletion {
96
85
  task_token: r1.task_token,
97
- task_queue: TEST_Q.to_string(),
98
86
  result: Some(ActivityExecutionResult::ok(vec![1].into()))
99
87
  }).await.unwrap();
100
88
  last_finisher.store(1, Ordering::SeqCst);
101
89
  },
102
90
  async {
103
- core.poll_activity_task(task_q).await.unwrap();
91
+ worker.poll_activity_task().await.unwrap();
104
92
  last_finisher.store(2, Ordering::SeqCst);
105
93
  }
106
94
  };
@@ -110,19 +98,14 @@ async fn max_activities_respected() {
110
98
 
111
99
  #[tokio::test]
112
100
  async fn activity_not_found_returns_ok() {
113
- let mut mock_gateway = mock_gateway();
101
+ let mut mock_client = mock_workflow_client();
114
102
  // Mock won't even be called, since we weren't tracking activity
115
- mock_gateway.expect_complete_activity_task().times(0);
103
+ mock_client.expect_complete_activity_task().times(0);
116
104
 
117
- let core = mock_core(MocksHolder::from_gateway_with_responses(
118
- mock_gateway,
119
- [],
120
- [],
121
- ));
105
+ let core = mock_worker(MocksHolder::from_client_with_responses(mock_client, [], []));
122
106
 
123
107
  core.complete_activity_task(ActivityTaskCompletion {
124
108
  task_token: vec![1],
125
- task_queue: TEST_Q.to_string(),
126
109
  result: Some(ActivityExecutionResult::ok(vec![1].into())),
127
110
  })
128
111
  .await
@@ -132,8 +115,8 @@ async fn activity_not_found_returns_ok() {
132
115
 
133
116
  #[tokio::test]
134
117
  async fn heartbeats_report_cancels_only_once() {
135
- let mut mock_gateway = mock_gateway();
136
- mock_gateway
118
+ let mut mock_client = mock_workflow_client();
119
+ mock_client
137
120
  .expect_record_activity_heartbeat()
138
121
  .times(2)
139
122
  .returning(|_, _| {
@@ -141,17 +124,17 @@ async fn heartbeats_report_cancels_only_once() {
141
124
  cancel_requested: true,
142
125
  })
143
126
  });
144
- mock_gateway
127
+ mock_client
145
128
  .expect_complete_activity_task()
146
129
  .times(1)
147
130
  .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
148
- mock_gateway
131
+ mock_client
149
132
  .expect_cancel_activity_task()
150
133
  .times(1)
151
134
  .returning(|_, _| Ok(RespondActivityTaskCanceledResponse::default()));
152
135
 
153
- let core = mock_core(MocksHolder::from_gateway_with_responses(
154
- mock_gateway,
136
+ let core = mock_worker(MocksHolder::from_client_with_responses(
137
+ mock_client,
155
138
  [],
156
139
  [
157
140
  PollActivityTaskQueueResponse {
@@ -169,15 +152,14 @@ async fn heartbeats_report_cancels_only_once() {
169
152
  ],
170
153
  ));
171
154
 
172
- let act = core.poll_activity_task(TEST_Q).await.unwrap();
155
+ let act = core.poll_activity_task().await.unwrap();
173
156
  core.record_activity_heartbeat(ActivityHeartbeat {
174
157
  task_token: act.task_token.clone(),
175
- task_queue: TEST_Q.to_string(),
176
158
  details: vec![vec![1_u8, 2, 3].into()],
177
159
  });
178
160
  // We have to wait a beat for the heartbeat to be processed
179
161
  sleep(Duration::from_millis(10)).await;
180
- let act = core.poll_activity_task(TEST_Q).await.unwrap();
162
+ let act = core.poll_activity_task().await.unwrap();
181
163
  assert_matches!(
182
164
  &act,
183
165
  ActivityTask {
@@ -192,7 +174,6 @@ async fn heartbeats_report_cancels_only_once() {
192
174
  sleep(Duration::from_millis(10)).await;
193
175
  core.record_activity_heartbeat(ActivityHeartbeat {
194
176
  task_token: act.task_token.clone(),
195
- task_queue: TEST_Q.to_string(),
196
177
  details: vec![vec![1_u8, 2, 3].into()],
197
178
  });
198
179
  // Wait delay again to flush heartbeat
@@ -200,14 +181,14 @@ async fn heartbeats_report_cancels_only_once() {
200
181
  // Now complete it as cancelled
201
182
  core.complete_activity_task(ActivityTaskCompletion {
202
183
  task_token: act.task_token,
203
- task_queue: TEST_Q.to_string(),
184
+
204
185
  result: Some(ActivityExecutionResult::cancel_from_details(None)),
205
186
  })
206
187
  .await
207
188
  .unwrap();
208
189
  // Since cancels always come before new tasks, if we get a new non-cancel task, we did not
209
190
  // double-issue cancels.
210
- let act = core.poll_activity_task(TEST_Q).await.unwrap();
191
+ let act = core.poll_activity_task().await.unwrap();
211
192
  assert_matches!(
212
193
  &act,
213
194
  ActivityTask {
@@ -219,7 +200,7 @@ async fn heartbeats_report_cancels_only_once() {
219
200
  // Complete it so shutdown goes through
220
201
  core.complete_activity_task(ActivityTaskCompletion {
221
202
  task_token: act.task_token,
222
- task_queue: TEST_Q.to_string(),
203
+
223
204
  result: Some(ActivityExecutionResult::ok(vec![1].into())),
224
205
  })
225
206
  .await
@@ -250,8 +231,8 @@ async fn activity_cancel_interrupts_poll() {
250
231
  .times(2)
251
232
  .returning(move || poll_resps.pop_front().unwrap());
252
233
 
253
- let mut mock_gateway = mock_manual_gateway();
254
- mock_gateway
234
+ let mut mock_client = mock_manual_workflow_client();
235
+ mock_client
255
236
  .expect_record_activity_heartbeat()
256
237
  .times(1)
257
238
  .returning(|_, _| {
@@ -262,37 +243,36 @@ async fn activity_cancel_interrupts_poll() {
262
243
  }
263
244
  .boxed()
264
245
  });
265
- mock_gateway
246
+ mock_client
266
247
  .expect_complete_activity_task()
267
248
  .times(1)
268
249
  .returning(|_, _| async { Ok(RespondActivityTaskCompletedResponse::default()) }.boxed());
269
250
 
270
- let mock_worker = MockWorker {
251
+ let mw = MockWorker {
271
252
  act_poller: Some(Box::from(mock_poller)),
272
253
  ..Default::default()
273
254
  };
274
-
275
- let core = mock_core(MocksHolder::from_mock_workers(mock_gateway, [mock_worker]));
255
+ let core = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
276
256
  let last_finisher = AtomicUsize::new(0);
277
257
  // Perform first poll to get the activity registered
278
- let act = core.poll_activity_task(TEST_Q).await.unwrap();
258
+ let act = core.poll_activity_task().await.unwrap();
279
259
  // Poll should block until heartbeat is sent, issuing the cancel, and interrupting the poll
280
260
  tokio::join! {
281
261
  async {
282
262
  core.record_activity_heartbeat(ActivityHeartbeat {
283
263
  task_token: act.task_token,
284
- task_queue: TEST_Q.to_string(),
264
+
285
265
  details: vec![vec![1_u8, 2, 3].into()],
286
266
  });
287
267
  last_finisher.store(1, Ordering::SeqCst);
288
268
  },
289
269
  async {
290
- let act = core.poll_activity_task(TEST_Q).await.unwrap();
270
+ let act = core.poll_activity_task().await.unwrap();
291
271
  // Must complete this activity for shutdown to finish
292
272
  core.complete_activity_task(
293
273
  ActivityTaskCompletion {
294
274
  task_token: act.task_token,
295
- task_queue: TEST_Q.to_string(),
275
+
296
276
  result: Some(ActivityExecutionResult::ok(vec![1].into())),
297
277
  }
298
278
  ).await.unwrap();
@@ -306,7 +286,7 @@ async fn activity_cancel_interrupts_poll() {
306
286
 
307
287
  #[tokio::test]
308
288
  async fn activity_poll_timeout_retries() {
309
- let mock_gateway = mock_gateway();
289
+ let mock_client = mock_workflow_client();
310
290
  let mut calls = 0;
311
291
  let mut mock_act_poller = mock_poller();
312
292
  mock_act_poller.expect_poll().times(3).returning(move || {
@@ -320,12 +300,12 @@ async fn activity_poll_timeout_retries() {
320
300
  }))
321
301
  }
322
302
  });
323
- let mock_worker = MockWorker {
303
+ let mw = MockWorker {
324
304
  act_poller: Some(Box::from(mock_act_poller)),
325
305
  ..Default::default()
326
306
  };
327
- let core = mock_core(MocksHolder::from_mock_workers(mock_gateway, [mock_worker]));
328
- let r = core.poll_activity_task(TEST_Q).await.unwrap();
307
+ let core = mock_worker(MocksHolder::from_mock_worker(mock_client.into(), mw));
308
+ let r = core.poll_activity_task().await.unwrap();
329
309
  assert_matches!(r.task_token.as_slice(), b"hello!");
330
310
  }
331
311
 
@@ -335,7 +315,7 @@ async fn many_concurrent_heartbeat_cancels() {
335
315
  // them after a few successful heartbeats
336
316
  const CONCURRENCY_NUM: usize = 5;
337
317
 
338
- let mut mock_gateway = mock_manual_gateway();
318
+ let mut mock_client = mock_manual_workflow_client();
339
319
  let mut poll_resps = VecDeque::from(
340
320
  (0..CONCURRENCY_NUM)
341
321
  .map(|i| {
@@ -361,13 +341,13 @@ async fn many_concurrent_heartbeat_cancels() {
361
341
  .boxed(),
362
342
  );
363
343
  let mut calls_map = HashMap::<_, i32>::new();
364
- mock_gateway
344
+ mock_client
365
345
  .expect_poll_activity_task()
366
- .returning(move |_| poll_resps.pop_front().unwrap());
367
- mock_gateway
346
+ .returning(move |_, _| poll_resps.pop_front().unwrap());
347
+ mock_client
368
348
  .expect_cancel_activity_task()
369
349
  .returning(move |_, _| async move { Ok(Default::default()) }.boxed());
370
- mock_gateway
350
+ mock_client
371
351
  .expect_record_activity_heartbeat()
372
352
  .returning(move |tt, _| {
373
353
  let calls = match calls_map.entry(tt) {
@@ -391,30 +371,27 @@ async fn many_concurrent_heartbeat_cancels() {
391
371
  .boxed()
392
372
  });
393
373
 
394
- let core = &mock_core_with_opts_no_workers(mock_gateway, CoreInitOptionsBuilder::default());
395
- core.register_worker(
396
- WorkerConfigBuilder::default()
397
- .task_queue(TEST_Q)
374
+ let worker = &Worker::new_test(
375
+ test_worker_cfg()
398
376
  .max_outstanding_activities(CONCURRENCY_NUM)
399
377
  // Only 1 poll at a time to avoid over-polling and running out of responses
400
378
  .max_concurrent_at_polls(1_usize)
401
379
  .build()
402
380
  .unwrap(),
403
- )
404
- .unwrap();
381
+ mock_client,
382
+ );
405
383
 
406
384
  // Poll all activities first so they are registered
407
385
  for _ in 0..CONCURRENCY_NUM {
408
- core.poll_activity_task(TEST_Q).await.unwrap();
386
+ worker.poll_activity_task().await.unwrap();
409
387
  }
410
388
 
411
389
  // Spawn "activities"
412
390
  fanout_tasks(CONCURRENCY_NUM, |i| async move {
413
391
  let task_token = i.to_be_bytes().to_vec();
414
392
  for _ in 0..12 {
415
- core.record_activity_heartbeat(ActivityHeartbeat {
393
+ worker.record_activity_heartbeat(ActivityHeartbeat {
416
394
  task_token: task_token.clone(),
417
- task_queue: TEST_Q.to_string(),
418
395
  details: vec![],
419
396
  });
420
397
  sleep(Duration::from_millis(50)).await;
@@ -424,7 +401,7 @@ async fn many_concurrent_heartbeat_cancels() {
424
401
 
425
402
  // Read all the cancellations and reply to them concurrently
426
403
  fanout_tasks(CONCURRENCY_NUM, |_| async move {
427
- let r = core.poll_activity_task(TEST_Q).await.unwrap();
404
+ let r = worker.poll_activity_task().await.unwrap();
428
405
  assert_matches!(
429
406
  r,
430
407
  ActivityTask {
@@ -432,23 +409,23 @@ async fn many_concurrent_heartbeat_cancels() {
432
409
  ..
433
410
  }
434
411
  );
435
- core.complete_activity_task(ActivityTaskCompletion {
436
- task_token: r.task_token.clone(),
437
- task_queue: TEST_Q.to_string(),
438
- result: Some(ActivityExecutionResult::cancel_from_details(None)),
439
- })
440
- .await
441
- .unwrap();
412
+ worker
413
+ .complete_activity_task(ActivityTaskCompletion {
414
+ task_token: r.task_token.clone(),
415
+ result: Some(ActivityExecutionResult::cancel_from_details(None)),
416
+ })
417
+ .await
418
+ .unwrap();
442
419
  })
443
420
  .await;
444
421
 
445
- core.shutdown().await;
422
+ worker.shutdown().await;
446
423
  }
447
424
 
448
425
  #[tokio::test]
449
426
  async fn activity_timeout_no_double_resolve() {
450
427
  let t = canned_histories::activity_double_resolve_repro();
451
- let core = build_fake_core("fake_wf_id", t, &[3]);
428
+ let core = build_fake_worker("fake_wf_id", t, &[3]);
452
429
  let activity_id = 1;
453
430
 
454
431
  poll_and_reply(
@@ -497,84 +474,10 @@ async fn activity_timeout_no_double_resolve() {
497
474
  core.shutdown().await;
498
475
  }
499
476
 
500
- #[tokio::test]
501
- async fn only_returns_cancels_for_desired_queue() {
502
- let mut mock_gateway = mock_gateway();
503
- let seen_cancel = Arc::new(Notify::new());
504
- let sc = seen_cancel.clone();
505
- mock_gateway
506
- .expect_record_activity_heartbeat()
507
- .times(1)
508
- .returning(move |_, _| {
509
- // Mark the activity as needing cancel when heartbeated
510
- sc.notify_one();
511
- Ok(RecordActivityTaskHeartbeatResponse {
512
- cancel_requested: true,
513
- })
514
- });
515
- mock_gateway
516
- .expect_cancel_activity_task()
517
- .times(1)
518
- .returning(|_, _| Ok(RespondActivityTaskCanceledResponse::default()));
519
-
520
- let mut w1 = MockWorker::for_queue("q1");
521
- w1.act_poller = Some(mock_poller_from_resps([PollActivityTaskQueueResponse {
522
- task_token: vec![1],
523
- activity_id: "act1".to_string(),
524
- heartbeat_timeout: Some(Duration::from_millis(1).into()),
525
- ..Default::default()
526
- }]));
527
- let mut mock_act_poller = mock_poller();
528
- mock_act_poller
529
- .expect_poll()
530
- .returning(|| Some(Ok(Default::default())));
531
- let mut w2 = MockWorker::for_queue("q2");
532
- w2.act_poller = Some(Box::from(mock_act_poller));
533
-
534
- let core = mock_core(MocksHolder::from_mock_workers(mock_gateway, [w1, w2]));
535
- // First poll should get the activity
536
- core.poll_activity_task("q1").await.unwrap();
537
- // Now record a heartbeat which will get the cancel response and mark the act as cancelled
538
- core.record_activity_heartbeat(ActivityHeartbeat {
539
- task_token: vec![1],
540
- task_queue: "q1".to_string(),
541
- details: vec![],
542
- });
543
- // Worker two's poll should never resolve, since it's just getting pretend long poll timeouts
544
- let q1poll = async {
545
- // Wait for cancel to propagate
546
- seen_cancel.notified().await;
547
- core.poll_activity_task("q1").await
548
- };
549
- let q1_res = tokio::select! {
550
- _ = core.poll_activity_task("q2") => panic!("q2 poll resolved!"),
551
- r = q1poll => {
552
- r.unwrap()
553
- }
554
- };
555
- assert_matches!(
556
- &q1_res,
557
- ActivityTask {
558
- variant: Some(activity_task::Variant::Cancel(_)),
559
- ..
560
- }
561
- );
562
- // act needs to complete to finalize shutdown
563
- core.complete_activity_task(ActivityTaskCompletion {
564
- task_token: q1_res.task_token,
565
- task_queue: "q1".to_string(),
566
- result: Some(ActivityExecutionResult::cancel_from_details(None)),
567
- })
568
- .await
569
- .unwrap();
570
-
571
- core.shutdown().await;
572
- }
573
-
574
477
  #[tokio::test]
575
478
  async fn can_heartbeat_acts_during_shutdown() {
576
- let mut mock_gateway = mock_gateway();
577
- mock_gateway
479
+ let mut mock_client = mock_workflow_client();
480
+ mock_client
578
481
  .expect_record_activity_heartbeat()
579
482
  .times(1)
580
483
  .returning(|_, _| {
@@ -582,13 +485,13 @@ async fn can_heartbeat_acts_during_shutdown() {
582
485
  cancel_requested: false,
583
486
  })
584
487
  });
585
- mock_gateway
488
+ mock_client
586
489
  .expect_complete_activity_task()
587
490
  .times(1)
588
491
  .returning(|_, _| Ok(RespondActivityTaskCompletedResponse::default()));
589
492
 
590
- let core = mock_core(MocksHolder::from_gateway_with_responses(
591
- mock_gateway,
493
+ let core = mock_worker(MocksHolder::from_client_with_responses(
494
+ mock_client,
592
495
  [],
593
496
  [PollActivityTaskQueueResponse {
594
497
  task_token: vec![1],
@@ -598,7 +501,7 @@ async fn can_heartbeat_acts_during_shutdown() {
598
501
  }],
599
502
  ));
600
503
 
601
- let act = core.poll_activity_task(TEST_Q).await.unwrap();
504
+ let act = core.poll_activity_task().await.unwrap();
602
505
  let complete_order = RefCell::new(vec![]);
603
506
  // Start shutdown before completing the activity
604
507
  let shutdown_fut = async {
@@ -608,12 +511,12 @@ async fn can_heartbeat_acts_during_shutdown() {
608
511
  let complete_fut = async {
609
512
  core.record_activity_heartbeat(ActivityHeartbeat {
610
513
  task_token: act.task_token.clone(),
611
- task_queue: TEST_Q.to_string(),
514
+
612
515
  details: vec![vec![1_u8, 2, 3].into()],
613
516
  });
614
517
  core.complete_activity_task(ActivityTaskCompletion {
615
518
  task_token: act.task_token,
616
- task_queue: TEST_Q.to_string(),
519
+
617
520
  result: Some(ActivityExecutionResult::ok(vec![1].into())),
618
521
  })
619
522
  .await
@@ -629,10 +532,10 @@ async fn can_heartbeat_acts_during_shutdown() {
629
532
  #[tokio::test]
630
533
  async fn complete_act_with_fail_flushes_heartbeat() {
631
534
  let last_hb = 50;
632
- let mut mock_gateway = mock_gateway();
535
+ let mut mock_client = mock_workflow_client();
633
536
  let last_seen_payload = Rc::new(RefCell::new(None));
634
537
  let lsp = last_seen_payload.clone();
635
- mock_gateway
538
+ mock_client
636
539
  .expect_record_activity_heartbeat()
637
540
  // Two times b/c we always record the first heartbeat, and we'll flush the last
638
541
  .times(2)
@@ -642,13 +545,13 @@ async fn complete_act_with_fail_flushes_heartbeat() {
642
545
  cancel_requested: false,
643
546
  })
644
547
  });
645
- mock_gateway
548
+ mock_client
646
549
  .expect_fail_activity_task()
647
550
  .times(1)
648
551
  .returning(|_, _| Ok(RespondActivityTaskFailedResponse::default()));
649
552
 
650
- let core = mock_core(MocksHolder::from_gateway_with_responses(
651
- mock_gateway,
553
+ let core = mock_worker(MocksHolder::from_client_with_responses(
554
+ mock_client,
652
555
  [],
653
556
  [PollActivityTaskQueueResponse {
654
557
  task_token: vec![1],
@@ -658,18 +561,16 @@ async fn complete_act_with_fail_flushes_heartbeat() {
658
561
  }],
659
562
  ));
660
563
 
661
- let act = core.poll_activity_task(TEST_Q).await.unwrap();
564
+ let act = core.poll_activity_task().await.unwrap();
662
565
  // Record a bunch of heartbeats
663
566
  for i in 1..=last_hb {
664
567
  core.record_activity_heartbeat(ActivityHeartbeat {
665
568
  task_token: act.task_token.clone(),
666
- task_queue: TEST_Q.to_string(),
667
569
  details: vec![vec![i].into()],
668
570
  });
669
571
  }
670
572
  core.complete_activity_task(ActivityTaskCompletion {
671
573
  task_token: act.task_token.clone(),
672
- task_queue: TEST_Q.to_string(),
673
574
  result: Some(ActivityExecutionResult::fail("Ahh".into())),
674
575
  })
675
576
  .await
@@ -680,3 +581,28 @@ async fn complete_act_with_fail_flushes_heartbeat() {
680
581
  let last_seen_payload = &last_seen_payload.take().unwrap().payloads[0];
681
582
  assert_eq!(last_seen_payload.data, &[last_hb]);
682
583
  }
584
+
585
+ #[tokio::test]
586
+ async fn max_tq_acts_set_passed_to_poll_properly() {
587
+ let rate = 9.28;
588
+ let mut mock_client = mock_workflow_client();
589
+ mock_client
590
+ .expect_poll_activity_task()
591
+ .returning(move |_, tps| {
592
+ assert_eq!(tps, Some(rate));
593
+ Ok(PollActivityTaskQueueResponse {
594
+ task_token: vec![1],
595
+ ..Default::default()
596
+ })
597
+ });
598
+
599
+ let cfg = WorkerConfigBuilder::default()
600
+ .namespace("enchi")
601
+ .task_queue("cat")
602
+ .max_concurrent_at_polls(1_usize)
603
+ .max_task_queue_activities_per_second(rate)
604
+ .build()
605
+ .unwrap();
606
+ let worker = Worker::new_test(cfg, mock_client);
607
+ worker.poll_activity_task().await.unwrap();
608
+ }