@temporalio/core-bridge 1.1.0 → 1.4.0

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 (124) hide show
  1. package/Cargo.lock +765 -128
  2. package/Cargo.toml +2 -2
  3. package/common.js +7 -3
  4. package/index.d.ts +118 -5
  5. package/index.js +2 -6
  6. package/package.json +2 -3
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/releases/x86_64-apple-darwin/index.node +0 -0
  10. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  11. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  12. package/scripts/build.js +4 -3
  13. package/sdk-core/.buildkite/docker/Dockerfile +2 -1
  14. package/sdk-core/.buildkite/pipeline.yml +2 -0
  15. package/sdk-core/.cargo/config.toml +1 -1
  16. package/sdk-core/ARCHITECTURE.md +2 -2
  17. package/sdk-core/README.md +12 -0
  18. package/sdk-core/bridge-ffi/Cargo.toml +2 -2
  19. package/sdk-core/bridge-ffi/src/lib.rs +2 -2
  20. package/sdk-core/client/Cargo.toml +7 -5
  21. package/sdk-core/client/src/lib.rs +354 -226
  22. package/sdk-core/client/src/metrics.rs +13 -11
  23. package/sdk-core/client/src/raw.rs +352 -107
  24. package/sdk-core/client/src/retry.rs +188 -147
  25. package/sdk-core/client/src/workflow_handle/mod.rs +1 -1
  26. package/sdk-core/core/Cargo.toml +28 -15
  27. package/sdk-core/core/src/core_tests/activity_tasks.rs +98 -33
  28. package/sdk-core/core/src/core_tests/child_workflows.rs +125 -3
  29. package/sdk-core/core/src/core_tests/local_activities.rs +6 -6
  30. package/sdk-core/core/src/core_tests/workers.rs +3 -2
  31. package/sdk-core/core/src/core_tests/workflow_tasks.rs +70 -2
  32. package/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  33. package/sdk-core/core/src/lib.rs +62 -28
  34. package/sdk-core/core/src/pollers/mod.rs +2 -0
  35. package/sdk-core/core/src/pollers/poll_buffer.rs +4 -4
  36. package/sdk-core/core/src/replay/mod.rs +3 -3
  37. package/sdk-core/core/src/retry_logic.rs +10 -9
  38. package/sdk-core/core/src/telemetry/metrics.rs +48 -39
  39. package/sdk-core/core/src/telemetry/mod.rs +46 -12
  40. package/sdk-core/core/src/telemetry/prometheus_server.rs +17 -13
  41. package/sdk-core/core/src/test_help/mod.rs +18 -8
  42. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +10 -10
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +13 -13
  44. package/sdk-core/core/src/worker/activities.rs +6 -12
  45. package/sdk-core/core/src/worker/client/mocks.rs +1 -0
  46. package/sdk-core/core/src/worker/client.rs +193 -64
  47. package/sdk-core/core/src/worker/mod.rs +14 -19
  48. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -0
  49. package/sdk-core/core/src/worker/workflow/history_update.rs +5 -5
  50. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +133 -85
  51. package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -2
  52. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +160 -105
  53. package/sdk-core/core/src/worker/workflow/managed_run.rs +2 -1
  54. package/sdk-core/core/src/worker/workflow/mod.rs +62 -58
  55. package/sdk-core/core/src/worker/workflow/run_cache.rs +5 -3
  56. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +7 -5
  57. package/sdk-core/core-api/Cargo.toml +3 -3
  58. package/sdk-core/core-api/src/errors.rs +3 -11
  59. package/sdk-core/core-api/src/worker.rs +7 -0
  60. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +1 -1
  61. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +1 -1
  62. package/sdk-core/protos/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +2 -6
  63. package/sdk-core/protos/api_upstream/.github/workflows/trigger-api-go-update.yml +29 -0
  64. package/sdk-core/protos/api_upstream/Makefile +2 -2
  65. package/sdk-core/protos/api_upstream/buf.yaml +1 -0
  66. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  67. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  68. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  69. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +7 -0
  70. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +14 -0
  71. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  72. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +18 -0
  73. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +57 -1
  74. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +1 -3
  75. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -2
  76. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +11 -0
  77. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +23 -0
  78. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  79. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +1 -0
  80. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +172 -0
  81. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -0
  82. package/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  83. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +18 -15
  84. package/sdk-core/protos/testsrv_upstream/Makefile +80 -0
  85. package/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  86. package/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  87. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  88. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  89. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  90. package/sdk-core/sdk/Cargo.toml +2 -2
  91. package/sdk-core/sdk/src/lib.rs +2 -2
  92. package/sdk-core/sdk/src/workflow_context/options.rs +36 -8
  93. package/sdk-core/sdk/src/workflow_context.rs +30 -6
  94. package/sdk-core/sdk/src/workflow_future.rs +4 -4
  95. package/sdk-core/sdk-core-protos/Cargo.toml +5 -5
  96. package/sdk-core/sdk-core-protos/build.rs +9 -1
  97. package/sdk-core/sdk-core-protos/src/history_builder.rs +6 -1
  98. package/sdk-core/sdk-core-protos/src/lib.rs +93 -32
  99. package/sdk-core/test-utils/Cargo.toml +3 -3
  100. package/sdk-core/test-utils/src/canned_histories.rs +58 -0
  101. package/sdk-core/test-utils/src/lib.rs +35 -12
  102. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  103. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +55 -5
  104. package/sdk-core/tests/integ_tests/polling_tests.rs +2 -1
  105. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -5
  106. package/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  107. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -10
  108. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  109. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +14 -14
  110. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +2 -6
  111. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +12 -12
  112. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +12 -1
  113. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -3
  114. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +8 -2
  115. package/sdk-core/tests/integ_tests/workflow_tests.rs +19 -4
  116. package/sdk-core/tests/load_tests.rs +2 -1
  117. package/sdk-core/tests/main.rs +17 -0
  118. package/sdk-core/tests/runner.rs +93 -0
  119. package/src/conversions.rs +157 -94
  120. package/src/helpers.rs +190 -0
  121. package/src/lib.rs +10 -912
  122. package/src/runtime.rs +436 -0
  123. package/src/testing.rs +67 -0
  124. package/src/worker.rs +465 -0
@@ -1,14 +1,14 @@
1
1
  use crate::{
2
- ClientOptions, RawClientLikeUser, Result, RetryConfig, WorkflowClientTrait, WorkflowOptions,
3
- WorkflowTaskCompletion,
2
+ ClientOptions, ListClosedFilters, ListOpenFilters, Namespace, Result, RetryConfig,
3
+ StartTimeFilter, WorkflowClientTrait, WorkflowOptions,
4
4
  };
5
- use backoff::{backoff::Backoff, ExponentialBackoff};
5
+ use backoff::{backoff::Backoff, exponential::ExponentialBackoff, Clock, SystemClock};
6
6
  use futures_retry::{ErrorHandler, FutureRetry, RetryPolicy};
7
- use std::{fmt::Debug, future::Future, time::Duration};
7
+ use std::{fmt::Debug, future::Future, sync::Arc, time::Duration};
8
8
  use temporal_sdk_core_protos::{
9
9
  coresdk::workflow_commands::QueryResult,
10
10
  temporal::api::{
11
- common::v1::{Payload, Payloads},
11
+ common::v1::{Header, Payload, Payloads},
12
12
  enums::v1::WorkflowTaskFailedCause,
13
13
  failure::v1::Failure,
14
14
  query::v1::WorkflowQuery,
@@ -28,21 +28,26 @@ pub const RETRYABLE_ERROR_CODES: [Code; 7] = [
28
28
  Code::OutOfRange,
29
29
  Code::Unavailable,
30
30
  ];
31
+ const LONG_POLL_FATAL_GRACE: Duration = Duration::from_secs(60);
32
+ /// Must match the method name in [crate::raw::WorkflowService]
33
+ const POLL_WORKFLOW_METH_NAME: &str = "poll_workflow_task_queue";
34
+ /// Must match the method name in [crate::raw::WorkflowService]
35
+ const POLL_ACTIVITY_METH_NAME: &str = "poll_activity_task_queue";
31
36
 
32
37
  /// A wrapper for a [WorkflowClientTrait] or [crate::WorkflowService] implementor which performs
33
38
  /// auto-retries
34
39
  #[derive(Debug, Clone)]
35
40
  pub struct RetryClient<SG> {
36
41
  client: SG,
37
- retry_config: RetryConfig,
42
+ retry_config: Arc<RetryConfig>,
38
43
  }
39
44
 
40
45
  impl<SG> RetryClient<SG> {
41
46
  /// Use the provided retry config with the provided client
42
- pub const fn new(client: SG, retry_config: RetryConfig) -> Self {
47
+ pub fn new(client: SG, retry_config: RetryConfig) -> Self {
43
48
  Self {
44
49
  client,
45
- retry_config,
50
+ retry_config: Arc::new(retry_config),
46
51
  }
47
52
  }
48
53
  }
@@ -81,9 +86,8 @@ impl<SG> RetryClient<SG> {
81
86
  }
82
87
 
83
88
  pub(crate) fn get_retry_config(&self, call_name: &'static str) -> RetryConfig {
84
- let call_type = Self::determine_call_type(call_name);
85
- match call_type {
86
- CallType::Normal => self.retry_config.clone(),
89
+ match CallType::from_call_name(call_name) {
90
+ CallType::Normal => (*self.retry_config).clone(),
87
91
  CallType::LongPoll => RetryConfig::poll_retry_policy(),
88
92
  }
89
93
  }
@@ -92,40 +96,39 @@ impl<SG> RetryClient<SG> {
92
96
  rtc: RetryConfig,
93
97
  factory: F,
94
98
  call_name: &'static str,
95
- ) -> FutureRetry<F, TonicErrorHandler>
99
+ ) -> FutureRetry<F, TonicErrorHandler<SystemClock>>
96
100
  where
97
101
  F: FnMut() -> Fut + Unpin,
98
102
  Fut: Future<Output = Result<R>>,
99
103
  {
100
- let call_type = Self::determine_call_type(call_name);
101
- FutureRetry::new(factory, TonicErrorHandler::new(rtc, call_type, call_name))
102
- }
103
-
104
- fn determine_call_type(call_name: &str) -> CallType {
105
- match call_name {
106
- "poll_workflow_task" | "poll_activity_task" => CallType::LongPoll,
107
- _ => CallType::Normal,
108
- }
104
+ FutureRetry::new(factory, TonicErrorHandler::new(rtc, call_name))
109
105
  }
110
106
  }
111
107
 
112
108
  #[derive(Debug)]
113
- pub(crate) struct TonicErrorHandler {
114
- backoff: ExponentialBackoff,
109
+ pub(crate) struct TonicErrorHandler<C: Clock> {
110
+ backoff: ExponentialBackoff<C>,
115
111
  max_retries: usize,
116
112
  call_type: CallType,
117
113
  call_name: &'static str,
118
114
  }
119
- impl TonicErrorHandler {
120
- fn new(cfg: RetryConfig, call_type: CallType, call_name: &'static str) -> Self {
115
+ impl TonicErrorHandler<SystemClock> {
116
+ fn new(cfg: RetryConfig, call_name: &'static str) -> Self {
117
+ Self::new_with_clock(cfg, call_name, SystemClock::default())
118
+ }
119
+ }
120
+ impl<C> TonicErrorHandler<C>
121
+ where
122
+ C: Clock,
123
+ {
124
+ fn new_with_clock(cfg: RetryConfig, call_name: &'static str, clock: C) -> Self {
121
125
  Self {
122
126
  max_retries: cfg.max_retries,
123
- call_type,
127
+ call_type: CallType::from_call_name(call_name),
124
128
  call_name,
125
- backoff: cfg.into(),
129
+ backoff: cfg.into_exp_backoff(clock),
126
130
  }
127
131
  }
128
-
129
132
  const fn should_log_retry_warning(&self, cur_attempt: usize) -> bool {
130
133
  // Warn on more than 5 retries for unlimited retrying
131
134
  if self.max_retries == 0 && cur_attempt > 5 {
@@ -144,8 +147,19 @@ pub enum CallType {
144
147
  Normal,
145
148
  LongPoll,
146
149
  }
150
+ impl CallType {
151
+ fn from_call_name(call_name: &str) -> Self {
152
+ match call_name {
153
+ POLL_WORKFLOW_METH_NAME | POLL_ACTIVITY_METH_NAME => CallType::LongPoll,
154
+ _ => CallType::Normal,
155
+ }
156
+ }
157
+ }
147
158
 
148
- impl ErrorHandler<tonic::Status> for TonicErrorHandler {
159
+ impl<C> ErrorHandler<tonic::Status> for TonicErrorHandler<C>
160
+ where
161
+ C: Clock,
162
+ {
149
163
  type OutError = tonic::Status;
150
164
 
151
165
  fn handle(&mut self, current_attempt: usize, e: tonic::Status) -> RetryPolicy<tonic::Status> {
@@ -154,10 +168,11 @@ impl ErrorHandler<tonic::Status> for TonicErrorHandler {
154
168
  return RetryPolicy::ForwardError(e);
155
169
  }
156
170
 
171
+ let is_long_poll = self.call_type == CallType::LongPoll;
157
172
  // Long polls are OK with being cancelled or running into the timeout because there's
158
173
  // nothing to do but retry anyway
159
- let long_poll_allowed = self.call_type == CallType::LongPoll
160
- && [Code::Cancelled, Code::DeadlineExceeded].contains(&e.code());
174
+ let long_poll_allowed =
175
+ is_long_poll && [Code::Cancelled, Code::DeadlineExceeded].contains(&e.code());
161
176
 
162
177
  if RETRYABLE_ERROR_CODES.contains(&e.code()) || long_poll_allowed {
163
178
  if current_attempt == 1 {
@@ -178,6 +193,10 @@ impl ErrorHandler<tonic::Status> for TonicErrorHandler {
178
193
  }
179
194
  }
180
195
  }
196
+ } else if is_long_poll && self.backoff.get_elapsed_time() <= LONG_POLL_FATAL_GRACE {
197
+ // We permit "fatal" errors while long polling for a while, because some proxies return
198
+ // stupid error codes while getting ready, among other weird infra issues
199
+ RetryPolicy::WaitRetry(self.backoff.max_interval)
181
200
  } else {
182
201
  RetryPolicy::ForwardError(e)
183
202
  }
@@ -208,6 +227,7 @@ where
208
227
  task_queue: String,
209
228
  workflow_id: String,
210
229
  workflow_type: String,
230
+ request_id: Option<String>,
211
231
  options: WorkflowOptions,
212
232
  ) -> Result<StartWorkflowExecutionResponse> {
213
233
  retry_call!(
@@ -217,31 +237,11 @@ where
217
237
  task_queue.clone(),
218
238
  workflow_id.clone(),
219
239
  workflow_type.clone(),
240
+ request_id.clone(),
220
241
  options.clone()
221
242
  )
222
243
  }
223
244
 
224
- async fn poll_workflow_task(
225
- &self,
226
- task_queue: String,
227
- is_sticky: bool,
228
- ) -> Result<PollWorkflowTaskQueueResponse> {
229
- retry_call!(self, poll_workflow_task, task_queue.clone(), is_sticky)
230
- }
231
-
232
- async fn poll_activity_task(
233
- &self,
234
- task_queue: String,
235
- max_tasks_per_sec: Option<f64>,
236
- ) -> Result<PollActivityTaskQueueResponse> {
237
- retry_call!(
238
- self,
239
- poll_activity_task,
240
- task_queue.clone(),
241
- max_tasks_per_sec
242
- )
243
- }
244
-
245
245
  async fn reset_sticky_task_queue(
246
246
  &self,
247
247
  workflow_id: String,
@@ -255,13 +255,6 @@ where
255
255
  )
256
256
  }
257
257
 
258
- async fn complete_workflow_task(
259
- &self,
260
- request: WorkflowTaskCompletion,
261
- ) -> Result<RespondWorkflowTaskCompletedResponse> {
262
- retry_call!(self, complete_workflow_task, request.clone())
263
- }
264
-
265
258
  async fn complete_activity_task(
266
259
  &self,
267
260
  task_token: TaskToken,
@@ -335,6 +328,7 @@ where
335
328
  run_id: String,
336
329
  signal_name: String,
337
330
  payloads: Option<Payloads>,
331
+ request_id: Option<String>,
338
332
  ) -> Result<SignalWorkflowExecutionResponse> {
339
333
  retry_call!(
340
334
  self,
@@ -342,7 +336,8 @@ where
342
336
  workflow_id.clone(),
343
337
  run_id.clone(),
344
338
  signal_name.clone(),
345
- payloads.clone()
339
+ payloads.clone(),
340
+ request_id.clone()
346
341
  )
347
342
  }
348
343
 
@@ -352,9 +347,11 @@ where
352
347
  task_queue: String,
353
348
  workflow_id: String,
354
349
  workflow_type: String,
350
+ request_id: Option<String>,
355
351
  options: WorkflowOptions,
356
352
  signal_name: String,
357
353
  signal_input: Option<Payloads>,
354
+ signal_header: Option<Header>,
358
355
  ) -> Result<SignalWithStartWorkflowExecutionResponse> {
359
356
  retry_call!(
360
357
  self,
@@ -363,9 +360,11 @@ where
363
360
  task_queue.clone(),
364
361
  workflow_id.clone(),
365
362
  workflow_type.clone(),
363
+ request_id.clone(),
366
364
  options.clone(),
367
365
  signal_name.clone(),
368
- signal_input.clone()
366
+ signal_input.clone(),
367
+ signal_header.clone()
369
368
  )
370
369
  }
371
370
 
@@ -430,13 +429,15 @@ where
430
429
  workflow_id: String,
431
430
  run_id: Option<String>,
432
431
  reason: String,
432
+ request_id: Option<String>,
433
433
  ) -> Result<RequestCancelWorkflowExecutionResponse> {
434
434
  retry_call!(
435
435
  self,
436
436
  cancel_workflow_execution,
437
437
  workflow_id.clone(),
438
438
  run_id.clone(),
439
- reason.clone()
439
+ reason.clone(),
440
+ request_id.clone()
440
441
  )
441
442
  }
442
443
 
@@ -457,6 +458,59 @@ where
457
458
  retry_call!(self, list_namespaces,)
458
459
  }
459
460
 
461
+ async fn describe_namespace(&self, namespace: Namespace) -> Result<DescribeNamespaceResponse> {
462
+ retry_call!(self, describe_namespace, namespace.clone())
463
+ }
464
+
465
+ async fn list_open_workflow_executions(
466
+ &self,
467
+ maximum_page_size: i32,
468
+ next_page_token: Vec<u8>,
469
+ start_time_filter: Option<StartTimeFilter>,
470
+ filters: Option<ListOpenFilters>,
471
+ ) -> Result<ListOpenWorkflowExecutionsResponse> {
472
+ retry_call!(
473
+ self,
474
+ list_open_workflow_executions,
475
+ maximum_page_size,
476
+ next_page_token.clone(),
477
+ start_time_filter.clone(),
478
+ filters.clone()
479
+ )
480
+ }
481
+
482
+ async fn list_closed_workflow_executions(
483
+ &self,
484
+ maximum_page_size: i32,
485
+ next_page_token: Vec<u8>,
486
+ start_time_filter: Option<StartTimeFilter>,
487
+ filters: Option<ListClosedFilters>,
488
+ ) -> Result<ListClosedWorkflowExecutionsResponse> {
489
+ retry_call!(
490
+ self,
491
+ list_closed_workflow_executions,
492
+ maximum_page_size,
493
+ next_page_token.clone(),
494
+ start_time_filter.clone(),
495
+ filters.clone()
496
+ )
497
+ }
498
+
499
+ async fn list_workflow_executions(
500
+ &self,
501
+ page_size: i32,
502
+ next_page_token: Vec<u8>,
503
+ query: String,
504
+ ) -> Result<ListWorkflowExecutionsResponse> {
505
+ retry_call!(
506
+ self,
507
+ list_workflow_executions,
508
+ page_size,
509
+ next_page_token.clone(),
510
+ query.clone()
511
+ )
512
+ }
513
+
460
514
  fn get_options(&self) -> &ClientOptions {
461
515
  self.client.get_options()
462
516
  }
@@ -466,21 +520,13 @@ where
466
520
  }
467
521
  }
468
522
 
469
- impl<C> RawClientLikeUser for RetryClient<C>
470
- where
471
- C: RawClientLikeUser,
472
- {
473
- type RawClientT = C::RawClientT;
474
-
475
- fn wf_svc(&self) -> Self::RawClientT {
476
- self.client.wf_svc()
477
- }
478
- }
479
-
480
523
  #[cfg(test)]
481
524
  mod tests {
482
525
  use super::*;
483
526
  use crate::MockWorkflowClientTrait;
527
+ use assert_matches::assert_matches;
528
+ use backoff::Clock;
529
+ use std::{ops::Add, time::Instant};
484
530
  use tonic::Status;
485
531
 
486
532
  #[tokio::test]
@@ -510,6 +556,13 @@ mod tests {
510
556
  }
511
557
  }
512
558
 
559
+ struct FixedClock(Instant);
560
+ impl Clock for FixedClock {
561
+ fn now(&self) -> Instant {
562
+ self.0
563
+ }
564
+ }
565
+
513
566
  #[tokio::test]
514
567
  async fn long_poll_non_retryable_errors() {
515
568
  for code in [
@@ -521,24 +574,48 @@ mod tests {
521
574
  Code::Unauthenticated,
522
575
  Code::Unimplemented,
523
576
  ] {
524
- let mut mock_client = MockWorkflowClientTrait::new();
525
- mock_client
526
- .expect_poll_workflow_task()
527
- .returning(move |_, _| Err(Status::new(code, "non-retryable failure")))
528
- .times(1);
529
- mock_client
530
- .expect_poll_activity_task()
531
- .returning(move |_, _| Err(Status::new(code, "non-retryable failure")))
532
- .times(1);
533
- let retry_client = RetryClient::new(mock_client, Default::default());
534
- let result = retry_client
535
- .poll_workflow_task("tq".to_string(), false)
536
- .await;
537
- assert!(result.is_err());
538
- let result = retry_client
539
- .poll_activity_task("tq".to_string(), None)
540
- .await;
541
- assert!(result.is_err());
577
+ for call_name in [POLL_WORKFLOW_METH_NAME, POLL_ACTIVITY_METH_NAME] {
578
+ let retry_cfg = RetryConfig::poll_retry_policy();
579
+ let mut err_handler = TonicErrorHandler {
580
+ max_retries: retry_cfg.max_retries,
581
+ call_type: CallType::LongPoll,
582
+ call_name,
583
+ backoff: retry_cfg.into_exp_backoff(FixedClock(Instant::now())),
584
+ };
585
+ let result = err_handler.handle(1, Status::new(code, "Ahh"));
586
+ assert_matches!(result, RetryPolicy::WaitRetry(_));
587
+ err_handler.backoff.clock.0 = err_handler
588
+ .backoff
589
+ .clock
590
+ .0
591
+ .add(LONG_POLL_FATAL_GRACE + Duration::from_secs(1));
592
+ let result = err_handler.handle(2, Status::new(code, "Ahh"));
593
+ assert_matches!(result, RetryPolicy::ForwardError(_));
594
+ }
595
+ }
596
+ }
597
+
598
+ #[tokio::test]
599
+ async fn long_poll_retryable_errors_never_fatal() {
600
+ for code in RETRYABLE_ERROR_CODES {
601
+ for call_name in [POLL_WORKFLOW_METH_NAME, POLL_ACTIVITY_METH_NAME] {
602
+ let retry_cfg = RetryConfig::poll_retry_policy();
603
+ let mut err_handler = TonicErrorHandler {
604
+ max_retries: retry_cfg.max_retries,
605
+ call_type: CallType::LongPoll,
606
+ call_name,
607
+ backoff: retry_cfg.into_exp_backoff(FixedClock(Instant::now())),
608
+ };
609
+ let result = err_handler.handle(1, Status::new(code, "Ahh"));
610
+ assert_matches!(result, RetryPolicy::WaitRetry(_));
611
+ err_handler.backoff.clock.0 = err_handler
612
+ .backoff
613
+ .clock
614
+ .0
615
+ .add(LONG_POLL_FATAL_GRACE + Duration::from_secs(1));
616
+ let result = err_handler.handle(2, Status::new(code, "Ahh"));
617
+ assert_matches!(result, RetryPolicy::WaitRetry(_));
618
+ }
542
619
  }
543
620
  }
544
621
 
@@ -566,68 +643,32 @@ mod tests {
566
643
 
567
644
  #[tokio::test]
568
645
  async fn long_poll_retries_forever() {
569
- let mut mock_client = MockWorkflowClientTrait::new();
570
- mock_client
571
- .expect_poll_workflow_task()
572
- .returning(move |_, _| Err(Status::new(Code::Unknown, "retryable failure")))
573
- .times(50);
574
- mock_client
575
- .expect_poll_workflow_task()
576
- .returning(|_, _| Ok(Default::default()))
577
- .times(1);
578
- mock_client
579
- .expect_poll_activity_task()
580
- .returning(move |_, _| Err(Status::new(Code::Unknown, "retryable failure")))
581
- .times(50);
582
- mock_client
583
- .expect_poll_activity_task()
584
- .returning(|_, _| Ok(Default::default()))
585
- .times(1);
586
-
587
- let retry_client = RetryClient::new(mock_client, Default::default());
588
-
589
- let result = retry_client
590
- .poll_workflow_task("tq".to_string(), false)
591
- .await;
592
- assert!(result.is_ok());
593
- let result = retry_client
594
- .poll_activity_task("tq".to_string(), None)
595
- .await;
596
- assert!(result.is_ok());
646
+ // A bit odd, but we don't need a real client to test the retry client passes through the
647
+ // correct retry config
648
+ let fake_retry = RetryClient::new((), Default::default());
649
+ for i in 1..=50 {
650
+ for call in [POLL_WORKFLOW_METH_NAME, POLL_ACTIVITY_METH_NAME] {
651
+ let mut err_handler =
652
+ TonicErrorHandler::new(fake_retry.get_retry_config(call), call);
653
+ let result = err_handler.handle(i, Status::new(Code::Unknown, "Ahh"));
654
+ assert_matches!(result, RetryPolicy::WaitRetry(_));
655
+ }
656
+ }
597
657
  }
598
658
 
599
659
  #[tokio::test]
600
660
  async fn long_poll_retries_deadline_exceeded() {
661
+ let fake_retry = RetryClient::new((), Default::default());
601
662
  // For some reason we will get cancelled in these situations occasionally (always?) too
602
663
  for code in [Code::Cancelled, Code::DeadlineExceeded] {
603
- let mut mock_client = MockWorkflowClientTrait::new();
604
- mock_client
605
- .expect_poll_workflow_task()
606
- .returning(move |_, _| Err(Status::new(code, "retryable failure")))
607
- .times(5);
608
- mock_client
609
- .expect_poll_workflow_task()
610
- .returning(|_, _| Ok(Default::default()))
611
- .times(1);
612
- mock_client
613
- .expect_poll_activity_task()
614
- .returning(move |_, _| Err(Status::new(code, "retryable failure")))
615
- .times(5);
616
- mock_client
617
- .expect_poll_activity_task()
618
- .returning(|_, _| Ok(Default::default()))
619
- .times(1);
620
-
621
- let retry_client = RetryClient::new(mock_client, Default::default());
622
-
623
- let result = retry_client
624
- .poll_workflow_task("tq".to_string(), false)
625
- .await;
626
- assert!(result.is_ok());
627
- let result = retry_client
628
- .poll_activity_task("tq".to_string(), None)
629
- .await;
630
- assert!(result.is_ok());
664
+ for call in [POLL_WORKFLOW_METH_NAME, POLL_ACTIVITY_METH_NAME] {
665
+ let mut err_handler =
666
+ TonicErrorHandler::new(fake_retry.get_retry_config(call), call);
667
+ for i in 1..=5 {
668
+ let result = err_handler.handle(i, Status::new(code, "retryable failure"));
669
+ assert_matches!(result, RetryPolicy::WaitRetry(_));
670
+ }
671
+ }
631
672
  }
632
673
  }
633
674
  }
@@ -100,7 +100,7 @@ where
100
100
  let server_res = self
101
101
  .client
102
102
  .clone()
103
- .client()
103
+ .workflow_client()
104
104
  .get_workflow_execution_history(GetWorkflowExecutionHistoryRequest {
105
105
  namespace: self.info.namespace.to_string(),
106
106
  execution: Some(WorkflowExecution {
@@ -23,39 +23,46 @@ dashmap = "5.0"
23
23
  derive_builder = "0.11"
24
24
  derive_more = "0.99"
25
25
  enum_dispatch = "0.3"
26
+ flate2 = "1.0"
26
27
  futures = "0.3"
27
- governor = "0.4"
28
+ futures-util = "0.3"
29
+ governor = "0.5"
28
30
  http = "0.2"
29
31
  hyper = "0.14"
30
32
  itertools = "0.10"
31
33
  lazy_static = "1.4"
32
34
  log = "0.4"
33
- lru = "0.7"
35
+ lru = "0.8"
34
36
  mockall = "0.11"
37
+ nix = "0.25"
35
38
  once_cell = "1.5"
36
- opentelemetry = { version = "0.17", features = ["rt-tokio"] }
37
- opentelemetry-otlp = { version = "0.10.0", features = ["tokio", "metrics"] }
38
- opentelemetry-prometheus = "0.10.0"
39
+ opentelemetry = { version = "0.18", features = ["rt-tokio"] }
40
+ opentelemetry-otlp = { version = "0.11", features = ["tokio", "metrics"] }
41
+ opentelemetry-prometheus = "0.11"
39
42
  parking_lot = { version = "0.12", features = ["send_guard"] }
40
43
  prometheus = "0.13"
41
- prost = "0.9"
42
- prost-types = "0.9"
44
+ prost = "0.11"
45
+ prost-types = "0.11"
43
46
  rand = "0.8.3"
47
+ reqwest = { version = "0.11", features = ["json", "stream", "rustls-tls", "tokio-rustls"], default-features = false }
44
48
  ringbuf = "0.2"
45
49
  serde = "1.0"
50
+ serde_json = "1.0"
46
51
  siphasher = "0.3"
47
52
  slotmap = "1.0"
53
+ tar = "0.4"
48
54
  thiserror = "1.0"
49
- tokio = { version = "1.1", features = ["rt", "rt-multi-thread", "parking_lot", "time", "fs"] }
50
- tokio-util = { version = "0.7" }
55
+ tokio = { version = "1.1", features = ["rt", "rt-multi-thread", "parking_lot", "time", "fs", "process"] }
56
+ tokio-util = { version = "0.7", features = ["io", "io-util"] }
51
57
  tokio-stream = "0.1"
52
- tonic = { version = "0.6", features = ["tls", "tls-roots"] }
58
+ tonic = { version = "0.8", features = ["tls", "tls-roots"] }
53
59
  tracing = { version = "0.1", features = ["log-always"] }
54
60
  tracing-futures = "0.2"
55
- tracing-opentelemetry = "0.17"
61
+ tracing-opentelemetry = "0.18"
56
62
  tracing-subscriber = { version = "0.3", features = ["parking_lot", "env-filter"] }
57
63
  url = "2.2"
58
- uuid = { version = "0.8.2", features = ["v4"] }
64
+ uuid = { version = "1.1", features = ["v4"] }
65
+ zip = "0.6"
59
66
 
60
67
  # 1st party local deps
61
68
  [dependencies.temporal-sdk-core-api]
@@ -78,13 +85,13 @@ version = "0.1"
78
85
  [dev-dependencies]
79
86
  assert_matches = "1.4"
80
87
  bimap = "0.6.1"
81
- criterion = "0.3"
82
- rstest = "0.12"
88
+ criterion = "0.4"
89
+ rstest = "0.15"
83
90
  temporal-sdk-core-test-utils = { path = "../test-utils" }
84
91
  temporal-sdk = { path = "../sdk" }
85
92
 
86
93
  [build-dependencies]
87
- tonic-build = "0.6"
94
+ tonic-build = "0.8"
88
95
 
89
96
  [[test]]
90
97
  name = "integ_tests"
@@ -101,3 +108,9 @@ test = false
101
108
  [[bench]]
102
109
  name = "workflow_replay"
103
110
  harness = false
111
+
112
+ # This is maybe a bit hacky, but we call the runner an "example" because that gets it compiling with
113
+ # the dev-dependencies, which we want.
114
+ [[example]]
115
+ name = "integ_runner"
116
+ path = "../tests/runner.rs"