@temporalio/core-bridge 1.5.2 → 1.6.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 (153) hide show
  1. package/Cargo.lock +255 -48
  2. package/package.json +4 -4
  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/.buildkite/pipeline.yml +1 -3
  9. package/sdk-core/.cargo/config.toml +5 -2
  10. package/sdk-core/.github/workflows/heavy.yml +28 -0
  11. package/sdk-core/Cargo.toml +1 -1
  12. package/sdk-core/README.md +9 -5
  13. package/sdk-core/client/src/lib.rs +211 -36
  14. package/sdk-core/client/src/raw.rs +1 -1
  15. package/sdk-core/client/src/retry.rs +32 -20
  16. package/sdk-core/core/Cargo.toml +23 -9
  17. package/sdk-core/core/src/abstractions.rs +11 -0
  18. package/sdk-core/core/src/core_tests/activity_tasks.rs +6 -5
  19. package/sdk-core/core/src/core_tests/local_activities.rs +263 -22
  20. package/sdk-core/core/src/core_tests/queries.rs +2 -2
  21. package/sdk-core/core/src/core_tests/workflow_tasks.rs +249 -5
  22. package/sdk-core/core/src/ephemeral_server/mod.rs +5 -6
  23. package/sdk-core/core/src/lib.rs +2 -0
  24. package/sdk-core/core/src/protosext/mod.rs +1 -1
  25. package/sdk-core/core/src/telemetry/log_export.rs +1 -1
  26. package/sdk-core/core/src/telemetry/mod.rs +23 -8
  27. package/sdk-core/core/src/test_help/mod.rs +8 -1
  28. package/sdk-core/core/src/worker/activities/local_activities.rs +259 -125
  29. package/sdk-core/core/src/worker/activities.rs +3 -2
  30. package/sdk-core/core/src/worker/mod.rs +53 -26
  31. package/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  32. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  33. package/sdk-core/core/src/worker/workflow/history_update.rs +835 -277
  34. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +9 -17
  35. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +3 -5
  36. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -2
  37. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +3 -5
  38. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -2
  39. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +1 -2
  40. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -2
  41. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +73 -51
  42. package/sdk-core/core/src/worker/workflow/machines/mod.rs +3 -3
  43. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +4 -4
  44. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -2
  45. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +3 -5
  46. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +6 -7
  47. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  48. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +4 -4
  49. package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  50. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +89 -58
  51. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +4 -7
  52. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +21 -9
  53. package/sdk-core/core/src/worker/workflow/managed_run.rs +1021 -360
  54. package/sdk-core/core/src/worker/workflow/mod.rs +306 -346
  55. package/sdk-core/core/src/worker/workflow/run_cache.rs +29 -53
  56. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  57. package/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  58. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +115 -0
  59. package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  60. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +444 -714
  61. package/sdk-core/core-api/Cargo.toml +2 -0
  62. package/sdk-core/core-api/src/errors.rs +1 -34
  63. package/sdk-core/core-api/src/lib.rs +6 -2
  64. package/sdk-core/core-api/src/worker.rs +14 -1
  65. package/sdk-core/etc/deps.svg +115 -140
  66. package/sdk-core/etc/regen-depgraph.sh +5 -0
  67. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +6 -6
  68. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -3
  69. package/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  70. package/sdk-core/protos/api_upstream/Makefile +5 -5
  71. package/sdk-core/protos/api_upstream/build/go.mod +7 -0
  72. package/sdk-core/protos/api_upstream/build/go.sum +5 -0
  73. package/sdk-core/protos/api_upstream/build/tools.go +29 -0
  74. package/sdk-core/protos/api_upstream/go.mod +6 -0
  75. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +9 -2
  76. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +12 -19
  77. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +2 -2
  78. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  79. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +3 -2
  80. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  81. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +3 -3
  82. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +20 -2
  83. package/sdk-core/protos/api_upstream/temporal/api/{update/v1/message.proto → enums/v1/interaction_type.proto} +11 -18
  84. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  85. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  86. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  87. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  88. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  89. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +2 -13
  90. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  91. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  92. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  93. package/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  94. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +13 -19
  95. package/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +87 -0
  96. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -2
  97. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +2 -2
  98. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -2
  99. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  100. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  101. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  102. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
  103. package/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  104. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -2
  105. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +13 -8
  106. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  107. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +2 -0
  108. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  109. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  110. package/sdk-core/sdk/Cargo.toml +4 -3
  111. package/sdk-core/sdk/src/lib.rs +87 -21
  112. package/sdk-core/sdk/src/workflow_future.rs +7 -12
  113. package/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  114. package/sdk-core/sdk-core-protos/build.rs +36 -2
  115. package/sdk-core/sdk-core-protos/src/history_builder.rs +26 -19
  116. package/sdk-core/sdk-core-protos/src/history_info.rs +4 -0
  117. package/sdk-core/sdk-core-protos/src/lib.rs +78 -34
  118. package/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  119. package/sdk-core/test-utils/Cargo.toml +3 -1
  120. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  121. package/sdk-core/test-utils/src/lib.rs +50 -18
  122. package/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  123. package/sdk-core/test-utils/src/workflows.rs +29 -0
  124. package/sdk-core/tests/fuzzy_workflow.rs +130 -0
  125. package/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +114 -7
  126. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -2
  127. package/sdk-core/tests/integ_tests/metrics_tests.rs +1 -1
  128. package/sdk-core/tests/integ_tests/polling_tests.rs +1 -39
  129. package/sdk-core/tests/integ_tests/queries_tests.rs +2 -127
  130. package/sdk-core/tests/integ_tests/visibility_tests.rs +52 -5
  131. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +74 -1
  132. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +5 -13
  133. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +1 -1
  134. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +2 -10
  135. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +69 -197
  136. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +4 -28
  137. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
  138. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +14 -14
  139. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +3 -19
  140. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +3 -19
  141. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +1 -1
  142. package/sdk-core/tests/integ_tests/workflow_tests.rs +5 -6
  143. package/sdk-core/tests/main.rs +2 -12
  144. package/sdk-core/tests/runner.rs +71 -34
  145. package/sdk-core/tests/wf_input_replay.rs +32 -0
  146. package/sdk-core/bridge-ffi/Cargo.toml +0 -24
  147. package/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  148. package/sdk-core/bridge-ffi/build.rs +0 -25
  149. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -224
  150. package/sdk-core/bridge-ffi/src/lib.rs +0 -746
  151. package/sdk-core/bridge-ffi/src/wrappers.rs +0 -221
  152. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  153. package/sdk-core/sdk/src/conversions.rs +0 -8
@@ -67,7 +67,7 @@ pub mod coresdk {
67
67
  fmt_tt(&self.task_token),
68
68
  )?;
69
69
  if let Some(r) = self.result.as_ref().and_then(|r| r.status.as_ref()) {
70
- write!(f, ", {}", r)?;
70
+ write!(f, ", {r}")?;
71
71
  } else {
72
72
  write!(f, ", missing result")?;
73
73
  }
@@ -125,13 +125,13 @@ pub mod coresdk {
125
125
  write!(f, "ActivityExecutionResult(")?;
126
126
  match self {
127
127
  aer::Status::Completed(v) => {
128
- write!(f, "{})", v)
128
+ write!(f, "{v})")
129
129
  }
130
130
  aer::Status::Failed(v) => {
131
- write!(f, "{})", v)
131
+ write!(f, "{v})")
132
132
  }
133
133
  aer::Status::Cancelled(v) => {
134
- write!(f, "{})", v)
134
+ write!(f, "{v})")
135
135
  }
136
136
  aer::Status::WillCompleteAsync(_) => {
137
137
  write!(f, "Will complete async)")
@@ -144,7 +144,7 @@ pub mod coresdk {
144
144
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
145
145
  write!(f, "Success(")?;
146
146
  if let Some(ref v) = self.result {
147
- write!(f, "{}", v)?;
147
+ write!(f, "{v}")?;
148
148
  }
149
149
  write!(f, ")")
150
150
  }
@@ -154,7 +154,7 @@ pub mod coresdk {
154
154
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155
155
  write!(f, "Failure(")?;
156
156
  if let Some(ref v) = self.failure {
157
- write!(f, "{}", v)?;
157
+ write!(f, "{v}")?;
158
158
  }
159
159
  write!(f, ")")
160
160
  }
@@ -164,7 +164,7 @@ pub mod coresdk {
164
164
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
165
165
  write!(f, "Cancellation(")?;
166
166
  if let Some(ref v) = self.failure {
167
- write!(f, "{}", v)?;
167
+ write!(f, "{v}")?;
168
168
  }
169
169
  write!(f, ")")
170
170
  }
@@ -197,11 +197,15 @@ pub mod coresdk {
197
197
  matches!(self.status, Some(activity_resolution::Status::Failed(_)))
198
198
  }
199
199
 
200
- pub fn timed_out(&self) -> bool {
201
- matches!(self.status, Some(activity_resolution::Status::Failed(Failure {
202
- failure: Some(ref f)
203
- })) if f.is_timeout()
204
- || f.cause.as_ref().map(|c| c.is_timeout()).unwrap_or_default())
200
+ pub fn timed_out(&self) -> Option<crate::temporal::api::enums::v1::TimeoutType> {
201
+ match self.status {
202
+ Some(activity_resolution::Status::Failed(Failure {
203
+ failure: Some(ref f),
204
+ })) => f
205
+ .is_timeout()
206
+ .or_else(|| f.cause.as_ref().and_then(|c| c.is_timeout())),
207
+ _ => None,
208
+ }
205
209
  }
206
210
 
207
211
  pub fn cancelled(&self) -> bool {
@@ -243,10 +247,6 @@ pub mod coresdk {
243
247
  }
244
248
  }
245
249
 
246
- pub mod bridge {
247
- tonic::include_proto!("coresdk.bridge");
248
- }
249
-
250
250
  pub mod common {
251
251
  tonic::include_proto!("coresdk.common");
252
252
  use super::external_data::LocalActivityMarkerData;
@@ -317,7 +317,7 @@ pub mod coresdk {
317
317
  }
318
318
 
319
319
  pub mod external_data {
320
- use prost_types::{Duration, Timestamp};
320
+ use prost_wkt_types::{Duration, Timestamp};
321
321
  use serde::{Deserialize, Deserializer, Serialize, Serializer};
322
322
  tonic::include_proto!("coresdk.external_data");
323
323
 
@@ -404,7 +404,7 @@ pub mod coresdk {
404
404
  query::v1::WorkflowQuery,
405
405
  },
406
406
  };
407
- use prost_types::Timestamp;
407
+ use prost_wkt_types::Timestamp;
408
408
  use std::{
409
409
  collections::HashMap,
410
410
  fmt::{Display, Formatter},
@@ -491,7 +491,7 @@ pub mod coresdk {
491
491
 
492
492
  impl Display for EvictionReason {
493
493
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
494
- write!(f, "{:?}", self)
494
+ write!(f, "{self:?}")
495
495
  }
496
496
  }
497
497
 
@@ -517,7 +517,7 @@ pub mod coresdk {
517
517
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
518
518
  match &self.variant {
519
519
  None => write!(f, "empty"),
520
- Some(v) => write!(f, "{}", v),
520
+ Some(v) => write!(f, "{v}"),
521
521
  }
522
522
  }
523
523
  }
@@ -673,7 +673,7 @@ pub mod coresdk {
673
673
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
674
674
  match &self.variant {
675
675
  None => write!(f, "Empty"),
676
- Some(v) => write!(f, "{}", v),
676
+ Some(v) => write!(f, "{v}"),
677
677
  }
678
678
  }
679
679
  }
@@ -1027,7 +1027,7 @@ pub mod coresdk {
1027
1027
  )?;
1028
1028
  match &self.status {
1029
1029
  None => write!(f, "empty")?,
1030
- Some(s) => write!(f, "{}", s)?,
1030
+ Some(s) => write!(f, "{s}")?,
1031
1031
  };
1032
1032
  write!(f, ")")
1033
1033
  }
@@ -1042,7 +1042,7 @@ pub mod coresdk {
1042
1042
  write!(f, "Success(")?;
1043
1043
  let mut written = 0;
1044
1044
  for c in commands {
1045
- write!(f, "{} ", c)?;
1045
+ write!(f, "{c} ")?;
1046
1046
  written += 1;
1047
1047
  if written >= 10 && written < commands.len() {
1048
1048
  write!(f, "... {} more", commands.len() - written)?;
@@ -1107,8 +1107,11 @@ pub mod coresdk {
1107
1107
  }
1108
1108
 
1109
1109
  impl Failure {
1110
- pub fn is_timeout(&self) -> bool {
1111
- matches!(self.failure_info, Some(FailureInfo::TimeoutFailureInfo(_)))
1110
+ pub fn is_timeout(&self) -> Option<crate::temporal::api::enums::v1::TimeoutType> {
1111
+ match &self.failure_info {
1112
+ Some(FailureInfo::TimeoutFailureInfo(ti)) => Some(ti.timeout_type()),
1113
+ _ => None,
1114
+ }
1112
1115
  }
1113
1116
 
1114
1117
  pub fn application_failure(message: String, non_retryable: bool) -> Self {
@@ -1124,6 +1127,26 @@ pub mod coresdk {
1124
1127
  }
1125
1128
  }
1126
1129
 
1130
+ pub fn application_failure_from_error(ae: anyhow::Error, non_retryable: bool) -> Self {
1131
+ Self {
1132
+ failure_info: Some(FailureInfo::ApplicationFailureInfo(
1133
+ ApplicationFailureInfo {
1134
+ non_retryable,
1135
+ ..Default::default()
1136
+ },
1137
+ )),
1138
+ ..ae.chain()
1139
+ .rfold(None, |cause, e| {
1140
+ Some(Self {
1141
+ message: e.to_string(),
1142
+ cause: cause.map(Box::new),
1143
+ ..Default::default()
1144
+ })
1145
+ })
1146
+ .unwrap_or_default()
1147
+ }
1148
+ }
1149
+
1127
1150
  /// Extracts an ApplicationFailureInfo from a Failure instance if it exists
1128
1151
  pub fn maybe_application_failure(&self) -> Option<&ApplicationFailureInfo> {
1129
1152
  if let Failure {
@@ -1194,7 +1217,7 @@ pub mod coresdk {
1194
1217
 
1195
1218
  impl From<anyhow::Error> for Failure {
1196
1219
  fn from(ae: anyhow::Error) -> Self {
1197
- Failure::application_failure(ae.to_string(), false)
1220
+ Failure::application_failure_from_error(ae, false)
1198
1221
  }
1199
1222
  }
1200
1223
 
@@ -1550,6 +1573,7 @@ pub mod temporal {
1550
1573
  }
1551
1574
  pub mod common {
1552
1575
  pub mod v1 {
1576
+ use base64::{prelude::BASE64_STANDARD, Engine};
1553
1577
  use std::{
1554
1578
  collections::HashMap,
1555
1579
  fmt::{Display, Formatter},
@@ -1586,11 +1610,11 @@ pub mod temporal {
1586
1610
  write!(
1587
1611
  f,
1588
1612
  "[{}..{}]",
1589
- base64::encode(windows.next().unwrap_or_default()),
1590
- base64::encode(windows.next_back().unwrap_or_default())
1613
+ BASE64_STANDARD.encode(windows.next().unwrap_or_default()),
1614
+ BASE64_STANDARD.encode(windows.next_back().unwrap_or_default())
1591
1615
  )
1592
1616
  } else {
1593
- write!(f, "[{}]", base64::encode(&self.data))
1617
+ write!(f, "[{}]", BASE64_STANDARD.encode(&self.data))
1594
1618
  }
1595
1619
  }
1596
1620
  }
@@ -1771,6 +1795,11 @@ pub mod temporal {
1771
1795
  }
1772
1796
  }
1773
1797
  }
1798
+ pub mod interaction {
1799
+ pub mod v1 {
1800
+ tonic::include_proto!("temporal.api.interaction.v1");
1801
+ }
1802
+ }
1774
1803
  pub mod namespace {
1775
1804
  pub mod v1 {
1776
1805
  tonic::include_proto!("temporal.api.namespace.v1");
@@ -1816,11 +1845,6 @@ pub mod temporal {
1816
1845
  tonic::include_proto!("temporal.api.testservice.v1");
1817
1846
  }
1818
1847
  }
1819
- pub mod update {
1820
- pub mod v1 {
1821
- tonic::include_proto!("temporal.api.update.v1");
1822
- }
1823
- }
1824
1848
  pub mod version {
1825
1849
  pub mod v1 {
1826
1850
  tonic::include_proto!("temporal.api.version.v1");
@@ -1930,3 +1954,23 @@ pub mod grpc {
1930
1954
  }
1931
1955
  }
1932
1956
  }
1957
+
1958
+ #[cfg(test)]
1959
+ mod tests {
1960
+ use crate::temporal::api::failure::v1::Failure;
1961
+ use anyhow::anyhow;
1962
+
1963
+ #[test]
1964
+ fn anyhow_to_failure_conversion() {
1965
+ let no_causes: Failure = anyhow!("no causes").into();
1966
+ assert_eq!(no_causes.cause, None);
1967
+ assert_eq!(no_causes.message, "no causes");
1968
+ let orig = anyhow!("fail 1");
1969
+ let mid = orig.context("fail 2");
1970
+ let top = mid.context("fail 3");
1971
+ let as_fail: Failure = top.into();
1972
+ assert_eq!(as_fail.message, "fail 3");
1973
+ assert_eq!(as_fail.cause.as_ref().unwrap().message, "fail 2");
1974
+ assert_eq!(as_fail.cause.unwrap().cause.unwrap().message, "fail 1");
1975
+ }
1976
+ }
@@ -1,8 +1,18 @@
1
+ use base64::{prelude::BASE64_STANDARD, Engine};
1
2
  use std::fmt::{Debug, Display, Formatter};
2
3
 
3
4
  static LOCAL_ACT_TASK_TOKEN_PREFIX: &[u8] = b"local_act_";
4
5
 
5
- #[derive(Hash, Eq, PartialEq, Clone, derive_more::From, derive_more::Into)]
6
+ #[derive(
7
+ Hash,
8
+ Eq,
9
+ PartialEq,
10
+ Clone,
11
+ derive_more::From,
12
+ derive_more::Into,
13
+ serde::Serialize,
14
+ serde::Deserialize,
15
+ )]
6
16
  /// Type-safe wrapper for task token bytes
7
17
  pub struct TaskToken(pub Vec<u8>);
8
18
 
@@ -34,5 +44,5 @@ impl Debug for TaskToken {
34
44
  }
35
45
 
36
46
  pub fn fmt_tt(tt: &[u8]) -> String {
37
- base64::encode(tt)
47
+ BASE64_STANDARD.encode(tt)
38
48
  }
@@ -11,7 +11,8 @@ path = "src/histfetch.rs"
11
11
  [dependencies]
12
12
  anyhow = "1.0"
13
13
  async-trait = "0.1"
14
- base64 = "0.13"
14
+ base64 = "0.21"
15
+ bytes = "1.3"
15
16
  futures = "0.3"
16
17
  log = "0.4"
17
18
  once_cell = "1.16"
@@ -19,6 +20,7 @@ parking_lot = "0.12"
19
20
  prost = "0.11"
20
21
  prost-types = "0.11"
21
22
  rand = "0.8"
23
+ rmp-serde = "1.1"
22
24
  serde_json = "1.0"
23
25
  temporal-client = { path = "../client" }
24
26
  temporal-sdk = { path = "../sdk" }
@@ -23,6 +23,6 @@ async fn main() -> Result<(), anyhow::Error> {
23
23
  .expect("history field must be populated");
24
24
  // Serialize history to file
25
25
  let byteified = hist.encode_to_vec();
26
- tokio::fs::write(format!("{}_history.bin", wf_id), &byteified).await?;
26
+ tokio::fs::write(format!("{wf_id}_history.bin"), &byteified).await?;
27
27
  Ok(())
28
28
  }
@@ -5,8 +5,14 @@
5
5
  extern crate tracing;
6
6
 
7
7
  pub mod canned_histories;
8
+ pub mod wf_input_saver;
9
+ pub mod workflows;
8
10
 
9
- use crate::stream::{Stream, TryStreamExt};
11
+ use crate::{
12
+ stream::{Stream, TryStreamExt},
13
+ wf_input_saver::stream_to_file,
14
+ };
15
+ use base64::{prelude::BASE64_STANDARD, Engine};
10
16
  use futures::{future, stream, stream::FuturesUnordered, StreamExt};
11
17
  use parking_lot::Mutex;
12
18
  use prost::Message;
@@ -45,7 +51,7 @@ use temporal_sdk_core_protos::{
45
51
  },
46
52
  temporal::api::{common::v1::Payload, history::v1::History},
47
53
  };
48
- use tokio::sync::OnceCell;
54
+ use tokio::sync::{mpsc::unbounded_channel, OnceCell};
49
55
  use url::Url;
50
56
 
51
57
  pub const NAMESPACE: &str = "default";
@@ -61,6 +67,15 @@ pub const INTEG_TEST_SERVER_USED_ENV_VAR: &str = "INTEG_TEST_SERVER_ON";
61
67
  const OTEL_URL_ENV_VAR: &str = "TEMPORAL_INTEG_OTEL_URL";
62
68
  /// If set, enable direct scraping of prom metrics on the specified port
63
69
  const PROM_ENABLE_ENV_VAR: &str = "TEMPORAL_INTEG_PROM_PORT";
70
+ #[macro_export]
71
+ macro_rules! prost_dur {
72
+ ($dur_call:ident $args:tt) => {
73
+ std::time::Duration::$dur_call$args
74
+ .try_into()
75
+ .expect("test duration fits")
76
+ };
77
+ }
78
+
64
79
  /// Create a worker instance which will use the provided test name to base the task queue and wf id
65
80
  /// upon. Returns the instance and the task queue name (which is also the workflow id).
66
81
  pub async fn init_core_and_create_wf(test_name: &str) -> CoreWfStarter {
@@ -82,6 +97,7 @@ pub fn init_core_replay_stream<I>(test_name: &str, histories: I) -> Arc<dyn Core
82
97
  where
83
98
  I: Stream<Item = HistoryForReplay> + Send + 'static,
84
99
  {
100
+ init_integ_telem();
85
101
  let worker_cfg = WorkerConfigBuilder::default()
86
102
  .namespace(NAMESPACE)
87
103
  .task_queue(test_name)
@@ -134,7 +150,8 @@ pub struct CoreWfStarter {
134
150
  /// Used for both the task queue and workflow id
135
151
  task_queue_name: String,
136
152
  pub worker_config: WorkerConfig,
137
- wft_timeout: Option<Duration>,
153
+ /// Options to use when starting workflow(s)
154
+ pub workflow_options: WorkflowOptions,
138
155
  initted_worker: OnceCell<InitializedWorker>,
139
156
  }
140
157
  struct InitializedWorker {
@@ -144,14 +161,10 @@ struct InitializedWorker {
144
161
 
145
162
  impl CoreWfStarter {
146
163
  pub fn new(test_name: &str) -> Self {
147
- let rand_bytes: Vec<u8> = rand::thread_rng().sample_iter(&Standard).take(6).collect();
148
- let task_q_salt = base64::encode(rand_bytes);
149
- let task_queue = format!("{}_{}", test_name, task_q_salt);
150
- Self::new_tq_name(&task_queue)
151
- }
152
-
153
- pub fn new_tq_name(task_queue: &str) -> Self {
154
164
  init_integ_telem();
165
+ let rand_bytes: Vec<u8> = rand::thread_rng().sample_iter(&Standard).take(6).collect();
166
+ let task_q_salt = BASE64_STANDARD.encode(rand_bytes);
167
+ let task_queue = format!("{test_name}_{task_q_salt}");
155
168
  Self {
156
169
  task_queue_name: task_queue.to_owned(),
157
170
  worker_config: WorkerConfigBuilder::default()
@@ -161,8 +174,8 @@ impl CoreWfStarter {
161
174
  .max_cached_workflows(1000_usize)
162
175
  .build()
163
176
  .unwrap(),
164
- wft_timeout: None,
165
177
  initted_worker: OnceCell::new(),
178
+ workflow_options: Default::default(),
166
179
  }
167
180
  }
168
181
 
@@ -189,13 +202,27 @@ impl CoreWfStarter {
189
202
  }
190
203
 
191
204
  /// Start the workflow defined by the builder and return run id
192
- pub async fn start_wf(&self) -> String {
193
- self.start_wf_with_id(self.task_queue_name.clone(), WorkflowOptions::default())
205
+ pub async fn start_wf(&mut self) -> String {
206
+ self.start_wf_with_id(self.task_queue_name.clone()).await
207
+ }
208
+
209
+ pub async fn start_with_worker(
210
+ &self,
211
+ wf_name: impl Into<String>,
212
+ worker: &mut TestWorker,
213
+ ) -> String {
214
+ worker
215
+ .submit_wf(
216
+ self.task_queue_name.clone(),
217
+ wf_name.into(),
218
+ vec![],
219
+ self.workflow_options.clone(),
220
+ )
194
221
  .await
222
+ .unwrap()
195
223
  }
196
224
 
197
- pub async fn start_wf_with_id(&self, workflow_id: String, mut opts: WorkflowOptions) -> String {
198
- opts.task_timeout = opts.task_timeout.or(self.wft_timeout);
225
+ pub async fn start_wf_with_id(&self, workflow_id: String) -> String {
199
226
  self.initted_worker
200
227
  .get()
201
228
  .expect(
@@ -209,7 +236,7 @@ impl CoreWfStarter {
209
236
  workflow_id,
210
237
  self.task_queue_name.clone(),
211
238
  None,
212
- opts,
239
+ self.workflow_options.clone(),
213
240
  )
214
241
  .await
215
242
  .unwrap()
@@ -274,8 +301,13 @@ impl CoreWfStarter {
274
301
  self
275
302
  }
276
303
 
277
- pub fn wft_timeout(&mut self, timeout: Duration) -> &mut Self {
278
- self.wft_timeout = Some(timeout);
304
+ pub fn enable_wf_state_input_recording(&mut self) -> &mut Self {
305
+ let (ser_tx, ser_rx) = unbounded_channel();
306
+ let worker_cfg_clone = self.worker_config.clone();
307
+ tokio::spawn(async move {
308
+ stream_to_file(&worker_cfg_clone, ser_rx).await.unwrap();
309
+ });
310
+ self.worker_config.wf_state_inputs = Some(ser_tx);
279
311
  self
280
312
  }
281
313
 
@@ -0,0 +1,50 @@
1
+ use anyhow::anyhow;
2
+ use bytes::BytesMut;
3
+ use futures::{stream::BoxStream, SinkExt, StreamExt};
4
+ use prost::bytes::Bytes;
5
+ use std::path::Path;
6
+ use temporal_sdk_core_api::worker::WorkerConfig;
7
+ use tokio::{fs::File, sync::mpsc::UnboundedReceiver};
8
+ use tokio_util::codec::{FramedRead, FramedWrite, LengthDelimitedCodec};
9
+
10
+ pub struct WFStateReplayData {
11
+ pub config: WorkerConfig,
12
+ pub inputs: BoxStream<'static, Result<BytesMut, std::io::Error>>,
13
+ }
14
+
15
+ pub async fn stream_to_file(
16
+ config: &WorkerConfig,
17
+ mut rcv: UnboundedReceiver<Vec<u8>>,
18
+ ) -> Result<(), anyhow::Error> {
19
+ let file = File::create("wf_inputs").await?;
20
+ let mut transport = FramedWrite::new(file, ldc());
21
+ // First write the worker config, since things like cache size affect how many evictions there
22
+ // will be, etc.
23
+ transport.send(rmp_serde::to_vec(config)?.into()).await?;
24
+ while let Some(v) = rcv.recv().await {
25
+ transport.send(Bytes::from(v)).await?;
26
+ }
27
+ Ok(())
28
+ }
29
+
30
+ pub async fn read_from_file(path: impl AsRef<Path>) -> Result<WFStateReplayData, anyhow::Error> {
31
+ let file = File::open(path).await?;
32
+ let mut framed_read = FramedRead::new(file, ldc());
33
+ // Deserialize the worker config first
34
+ let config = framed_read
35
+ .next()
36
+ .await
37
+ .ok_or_else(|| anyhow!("Replay data file is empty"))??;
38
+ let config = rmp_serde::from_slice(config.as_ref())?;
39
+
40
+ Ok(WFStateReplayData {
41
+ config,
42
+ inputs: framed_read.boxed(),
43
+ })
44
+ }
45
+
46
+ fn ldc() -> LengthDelimitedCodec {
47
+ LengthDelimitedCodec::builder()
48
+ .length_field_type::<u16>()
49
+ .new_codec()
50
+ }
@@ -0,0 +1,29 @@
1
+ use crate::prost_dur;
2
+ use std::time::Duration;
3
+ use temporal_sdk::{ActivityOptions, LocalActivityOptions, WfContext, WorkflowResult};
4
+ use temporal_sdk_core_protos::{coresdk::AsJsonPayloadExt, temporal::api::common::v1::RetryPolicy};
5
+
6
+ pub async fn la_problem_workflow(ctx: WfContext) -> WorkflowResult<()> {
7
+ ctx.local_activity(LocalActivityOptions {
8
+ activity_type: "delay".to_string(),
9
+ input: "hi".as_json_payload().expect("serializes fine"),
10
+ retry_policy: RetryPolicy {
11
+ initial_interval: Some(prost_dur!(from_micros(15))),
12
+ backoff_coefficient: 1_000.,
13
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
14
+ maximum_attempts: 4,
15
+ non_retryable_error_types: vec![],
16
+ },
17
+ timer_backoff_threshold: Some(Duration::from_secs(1)),
18
+ ..Default::default()
19
+ })
20
+ .await;
21
+ ctx.activity(ActivityOptions {
22
+ activity_type: "delay".to_string(),
23
+ start_to_close_timeout: Some(Duration::from_secs(20)),
24
+ input: "hi!".as_json_payload().expect("serializes fine"),
25
+ ..Default::default()
26
+ })
27
+ .await;
28
+ Ok(().into())
29
+ }
@@ -0,0 +1,130 @@
1
+ use futures_util::{sink, stream::FuturesUnordered, FutureExt, StreamExt};
2
+ use rand::{prelude::Distribution, rngs::SmallRng, Rng, SeedableRng};
3
+ use std::{future, time::Duration};
4
+ use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowOptions};
5
+ use temporal_sdk::{ActContext, ActivityOptions, LocalActivityOptions, WfContext, WorkflowResult};
6
+ use temporal_sdk_core_protos::coresdk::{AsJsonPayloadExt, FromJsonPayloadExt, IntoPayloadsExt};
7
+ use temporal_sdk_core_test_utils::CoreWfStarter;
8
+ use tokio_util::sync::CancellationToken;
9
+
10
+ const FUZZY_SIG: &str = "fuzzy_sig";
11
+
12
+ #[derive(serde::Serialize, serde::Deserialize, Copy, Clone)]
13
+ enum FuzzyWfAction {
14
+ Shutdown,
15
+ DoAct,
16
+ DoLocalAct,
17
+ }
18
+
19
+ struct FuzzyWfActionSampler;
20
+ impl Distribution<FuzzyWfAction> for FuzzyWfActionSampler {
21
+ fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> FuzzyWfAction {
22
+ let v: u8 = rng.gen_range(1..=2);
23
+ match v {
24
+ 1 => FuzzyWfAction::DoAct,
25
+ 2 => FuzzyWfAction::DoLocalAct,
26
+ _ => unreachable!(),
27
+ }
28
+ }
29
+ }
30
+
31
+ async fn echo(_ctx: ActContext, echo_me: String) -> Result<String, anyhow::Error> {
32
+ Ok(echo_me)
33
+ }
34
+
35
+ async fn fuzzy_wf_def(ctx: WfContext) -> WorkflowResult<()> {
36
+ let sigchan = ctx
37
+ .make_signal_channel(FUZZY_SIG)
38
+ .map(|sd| FuzzyWfAction::from_json_payload(&sd.input[0]).expect("Can deserialize signal"));
39
+ let done = CancellationToken::new();
40
+ let done_setter = done.clone();
41
+
42
+ sigchan
43
+ .take_until(done.cancelled())
44
+ .for_each_concurrent(None, |action| {
45
+ let fut = match action {
46
+ FuzzyWfAction::DoAct => ctx
47
+ .activity(ActivityOptions {
48
+ activity_type: "echo_activity".to_string(),
49
+ start_to_close_timeout: Some(Duration::from_secs(5)),
50
+ input: "hi!".as_json_payload().expect("serializes fine"),
51
+ ..Default::default()
52
+ })
53
+ .map(|_| ())
54
+ .boxed(),
55
+ FuzzyWfAction::DoLocalAct => ctx
56
+ .local_activity(LocalActivityOptions {
57
+ activity_type: "echo_activity".to_string(),
58
+ start_to_close_timeout: Some(Duration::from_secs(5)),
59
+ input: "hi!".as_json_payload().expect("serializes fine"),
60
+ ..Default::default()
61
+ })
62
+ .map(|_| ())
63
+ .boxed(),
64
+ FuzzyWfAction::Shutdown => {
65
+ done_setter.cancel();
66
+ future::ready(()).boxed()
67
+ }
68
+ };
69
+ fut
70
+ })
71
+ .await;
72
+
73
+ Ok(().into())
74
+ }
75
+
76
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
77
+ async fn fuzzy_workflow() {
78
+ let num_workflows = 200;
79
+ let wf_name = "fuzzy_wf";
80
+ let mut starter = CoreWfStarter::new("fuzzy_workflow");
81
+ starter.max_wft(25).max_cached_workflows(25).max_at(25);
82
+ // .enable_wf_state_input_recording();
83
+ let mut worker = starter.worker().await;
84
+ worker.register_wf(wf_name.to_owned(), fuzzy_wf_def);
85
+ worker.register_activity("echo_activity", echo);
86
+ let client = starter.get_client().await;
87
+
88
+ let mut workflow_handles = vec![];
89
+ for i in 0..num_workflows {
90
+ let wfid = format!("{wf_name}_{i}");
91
+ let rid = worker
92
+ .submit_wf(
93
+ wfid.clone(),
94
+ wf_name.to_owned(),
95
+ vec![],
96
+ WorkflowOptions::default(),
97
+ )
98
+ .await
99
+ .unwrap();
100
+ workflow_handles.push(client.get_untyped_workflow_handle(wfid, rid));
101
+ }
102
+
103
+ let rng = SmallRng::seed_from_u64(523189);
104
+ let mut actions: Vec<FuzzyWfAction> = rng.sample_iter(FuzzyWfActionSampler).take(15).collect();
105
+ actions.push(FuzzyWfAction::Shutdown);
106
+
107
+ let sig_sender = async {
108
+ for action in actions {
109
+ let sends: FuturesUnordered<_> = (0..num_workflows)
110
+ .map(|i| {
111
+ client.signal_workflow_execution(
112
+ format!("{wf_name}_{i}"),
113
+ "".to_string(),
114
+ FUZZY_SIG.to_string(),
115
+ [action.as_json_payload().expect("Serializes ok")].into_payloads(),
116
+ None,
117
+ )
118
+ })
119
+ .collect();
120
+ sends
121
+ .map(|_| Ok(()))
122
+ .forward(sink::drain())
123
+ .await
124
+ .expect("Sending signals works");
125
+ tokio::time::sleep(Duration::from_secs(1)).await;
126
+ }
127
+ };
128
+ let (r1, _) = tokio::join!(worker.run_until_done(), sig_sender);
129
+ r1.unwrap();
130
+ }