@temporalio/core-bridge 0.16.4 → 0.18.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 (170) hide show
  1. package/Cargo.lock +339 -226
  2. package/Cargo.toml +7 -3
  3. package/common.js +50 -0
  4. package/index.d.ts +7 -0
  5. package/index.js +12 -0
  6. package/package.json +7 -4
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/{index.node → releases/index.node} +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/scripts/build.js +10 -50
  14. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  15. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  16. package/sdk-core/.buildkite/pipeline.yml +2 -0
  17. package/sdk-core/Cargo.toml +1 -88
  18. package/sdk-core/README.md +30 -6
  19. package/sdk-core/bridge-ffi/Cargo.toml +24 -0
  20. package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  21. package/sdk-core/bridge-ffi/build.rs +25 -0
  22. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
  23. package/sdk-core/bridge-ffi/src/lib.rs +829 -0
  24. package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
  25. package/sdk-core/client/Cargo.toml +32 -0
  26. package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
  27. package/sdk-core/client/src/metrics.rs +89 -0
  28. package/sdk-core/client/src/mocks.rs +167 -0
  29. package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
  30. package/sdk-core/core/Cargo.toml +96 -0
  31. package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
  32. package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
  33. package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
  34. package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
  35. package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
  36. package/sdk-core/{src → core/src}/core_tests/queries.rs +54 -54
  37. package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
  38. package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
  39. package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
  40. package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +280 -292
  41. package/sdk-core/core/src/lib.rs +374 -0
  42. package/sdk-core/{src → core/src}/log_export.rs +3 -27
  43. package/sdk-core/core/src/pending_activations.rs +162 -0
  44. package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
  45. package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
  46. package/sdk-core/core/src/protosext/mod.rs +396 -0
  47. package/sdk-core/core/src/replay/mod.rs +210 -0
  48. package/sdk-core/core/src/retry_logic.rs +144 -0
  49. package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
  50. package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
  51. package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
  52. package/sdk-core/{src → core/src}/test_help/mod.rs +35 -83
  53. package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
  54. package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
  55. package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
  56. package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
  57. package/sdk-core/{src → core/src}/worker/mod.rs +347 -221
  58. package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
  59. package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
  60. package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
  61. package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
  62. package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
  63. package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
  64. package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
  65. package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +20 -31
  66. package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
  67. package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
  68. package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
  69. package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
  70. package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
  71. package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  72. package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
  73. package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
  74. package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
  75. package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
  76. package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
  77. package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
  78. package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
  79. package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +357 -171
  80. package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
  81. package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
  82. package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
  83. package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
  84. package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +317 -103
  85. package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
  86. package/sdk-core/{src → core-api/src}/errors.rs +42 -92
  87. package/sdk-core/core-api/src/lib.rs +158 -0
  88. package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
  89. package/sdk-core/etc/deps.svg +156 -0
  90. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
  92. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
  93. package/sdk-core/histories/fail_wf_task.bin +0 -0
  94. package/sdk-core/histories/timer_workflow_history.bin +0 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
  96. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
  97. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
  98. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
  103. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
  104. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  105. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
  106. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
  107. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
  108. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
  109. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  110. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
  111. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
  112. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
  113. package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
  114. package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
  115. package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
  116. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  117. package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
  118. package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
  119. package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
  120. package/sdk-core/sdk/Cargo.toml +32 -0
  121. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
  122. package/sdk-core/sdk/src/lib.rs +699 -0
  123. package/sdk-core/sdk/src/payload_converter.rs +11 -0
  124. package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
  125. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
  126. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
  127. package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
  128. package/sdk-core/sdk-core-protos/build.rs +28 -6
  129. package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  130. package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
  132. package/sdk-core/sdk-core-protos/src/lib.rs +601 -168
  133. package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  134. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  135. package/sdk-core/test-utils/Cargo.toml +32 -0
  136. package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
  137. package/sdk-core/test-utils/src/histfetch.rs +28 -0
  138. package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
  139. package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
  140. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
  141. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
  142. package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
  143. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
  145. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
  147. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
  148. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
  149. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
  150. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
  151. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
  152. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
  153. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
  154. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
  155. package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
  156. package/sdk-core/tests/load_tests.rs +6 -6
  157. package/sdk-core/tests/main.rs +2 -2
  158. package/src/conversions.rs +24 -21
  159. package/src/errors.rs +8 -0
  160. package/src/lib.rs +323 -211
  161. package/sdk-core/protos/local/activity_result.proto +0 -46
  162. package/sdk-core/protos/local/activity_task.proto +0 -66
  163. package/sdk-core/src/core_tests/retry.rs +0 -147
  164. package/sdk-core/src/lib.rs +0 -403
  165. package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
  166. package/sdk-core/src/pending_activations.rs +0 -249
  167. package/sdk-core/src/protosext/mod.rs +0 -160
  168. package/sdk-core/src/prototype_rust_sdk.rs +0 -412
  169. package/sdk-core/src/task_token.rs +0 -20
  170. package/sdk-core/src/test_help/history_info.rs +0 -157
@@ -0,0 +1,328 @@
1
+ use crate::{
2
+ replay::{default_wes_attribs, TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE},
3
+ test_help::{build_mock_pollers, mock_core, MockPollCfg, ResponseType, TEST_Q},
4
+ Core,
5
+ };
6
+ use anyhow::anyhow;
7
+ use futures::future::join_all;
8
+ use std::{
9
+ sync::{
10
+ atomic::{AtomicUsize, Ordering},
11
+ Arc,
12
+ },
13
+ time::Duration,
14
+ };
15
+ use temporal_client::mocks::mock_gateway;
16
+ use temporal_sdk::{LocalActivityOptions, TestRustWorker, WfContext, WorkflowResult};
17
+ use temporal_sdk_core_protos::{
18
+ coresdk::{common::RetryPolicy, AsJsonPayloadExt},
19
+ temporal::api::{enums::v1::EventType, failure::v1::Failure},
20
+ };
21
+ use tokio::sync::Barrier;
22
+
23
+ async fn echo(e: String) -> anyhow::Result<String> {
24
+ Ok(e)
25
+ }
26
+
27
+ /// This test verifies that when replaying we are able to resolve local activities whose data we
28
+ /// don't see until after the workflow issues the command
29
+ #[rstest::rstest]
30
+ #[case::replay(true, true)]
31
+ #[case::not_replay(false, true)]
32
+ #[case::replay_cache_off(true, false)]
33
+ #[case::not_replay_cache_off(false, false)]
34
+ #[tokio::test]
35
+ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached: bool) {
36
+ let mut t = TestHistoryBuilder::default();
37
+ t.add_by_type(EventType::WorkflowExecutionStarted);
38
+ t.add_full_wf_task();
39
+ let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
40
+ t.add_full_wf_task();
41
+ t.add_local_activity_result_marker(1, "1", b"echo".into());
42
+ t.add_timer_fired(timer_started_event_id, "1".to_string());
43
+ t.add_full_wf_task();
44
+ t.add_workflow_execution_completed();
45
+
46
+ let wf_id = "fakeid";
47
+ let mock = mock_gateway();
48
+ let resps = if replay {
49
+ vec![ResponseType::AllHistory]
50
+ } else {
51
+ vec![1.into(), 2.into(), ResponseType::AllHistory]
52
+ };
53
+ let mh = MockPollCfg::from_resp_batches(wf_id, t, resps, mock);
54
+ let mut mock = build_mock_pollers(mh);
55
+ if cached {
56
+ mock.worker_cfg(TEST_Q, |cfg| cfg.max_cached_workflows = 1);
57
+ }
58
+ let core = mock_core(mock);
59
+ let mut worker = TestRustWorker::new(Arc::new(core), TEST_Q.to_string(), None);
60
+
61
+ worker.register_wf(
62
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
63
+ |ctx: WfContext| async move {
64
+ let la = ctx.local_activity(LocalActivityOptions {
65
+ activity_type: "echo".to_string(),
66
+ input: "hi".as_json_payload().expect("serializes fine"),
67
+ ..Default::default()
68
+ });
69
+ ctx.timer(Duration::from_secs(1)).await;
70
+ la.await;
71
+ Ok(().into())
72
+ },
73
+ );
74
+ worker.register_activity("echo", echo);
75
+ worker
76
+ .submit_wf(wf_id.to_owned(), DEFAULT_WORKFLOW_TYPE.to_owned(), vec![])
77
+ .await
78
+ .unwrap();
79
+ worker.run_until_done().await.unwrap();
80
+ }
81
+
82
+ pub async fn local_act_fanout_wf(ctx: WfContext) -> WorkflowResult<()> {
83
+ let las: Vec<_> = (1..=50)
84
+ .map(|i| {
85
+ ctx.local_activity(LocalActivityOptions {
86
+ activity_type: "echo".to_string(),
87
+ input: format!("Hi {}", i)
88
+ .as_json_payload()
89
+ .expect("serializes fine"),
90
+ ..Default::default()
91
+ })
92
+ })
93
+ .collect();
94
+ ctx.timer(Duration::from_secs(1)).await;
95
+ join_all(las).await;
96
+ Ok(().into())
97
+ }
98
+
99
+ #[tokio::test]
100
+ async fn local_act_many_concurrent() {
101
+ let mut t = TestHistoryBuilder::default();
102
+ t.add_by_type(EventType::WorkflowExecutionStarted);
103
+ t.add_full_wf_task();
104
+ let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
105
+ t.add_full_wf_task();
106
+ for i in 1..=50 {
107
+ t.add_local_activity_result_marker(i, &i.to_string(), b"echo".into());
108
+ }
109
+ t.add_timer_fired(timer_started_event_id, "1".to_string());
110
+ t.add_full_wf_task();
111
+ t.add_workflow_execution_completed();
112
+
113
+ let wf_id = "fakeid";
114
+ let mock = mock_gateway();
115
+ let mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
116
+ let mock = build_mock_pollers(mh);
117
+ let core = mock_core(mock);
118
+ let mut worker = TestRustWorker::new(Arc::new(core), TEST_Q.to_string(), None);
119
+
120
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE.to_owned(), local_act_fanout_wf);
121
+ worker.register_activity("echo", |str: String| async move { Ok(str) });
122
+ worker
123
+ .submit_wf(wf_id.to_owned(), DEFAULT_WORKFLOW_TYPE.to_owned(), vec![])
124
+ .await
125
+ .unwrap();
126
+ worker.run_until_done().await.unwrap();
127
+ }
128
+
129
+ /// Verifies that local activities which take more than a workflow task timeout will cause
130
+ /// us to issue additional (empty) WFT completions with the force flag on, thus preventing timeout
131
+ /// of WFT while the local activity continues to execute.
132
+ ///
133
+ /// The test with shutdown verifies if we call shutdown while the local activity is running that
134
+ /// shutdown does not complete until it's finished.
135
+ #[rstest::rstest]
136
+ #[case::with_shutdown(true)]
137
+ #[case::normal_complete(false)]
138
+ #[tokio::test]
139
+ async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
140
+ let mut t = TestHistoryBuilder::default();
141
+ let wft_timeout = Duration::from_millis(200);
142
+ let mut wes_short_wft_timeout = default_wes_attribs();
143
+ wes_short_wft_timeout.workflow_task_timeout = Some(wft_timeout.into());
144
+ t.add(
145
+ EventType::WorkflowExecutionStarted,
146
+ wes_short_wft_timeout.into(),
147
+ );
148
+ t.add_full_wf_task();
149
+ // Task created by WFT heartbeat
150
+ t.add_full_wf_task();
151
+ t.add_workflow_task_scheduled_and_started();
152
+
153
+ let wf_id = "fakeid";
154
+ let mock = mock_gateway();
155
+ // Allow returning incomplete history more than once, as the wft timeout can be timing sensitive
156
+ // and might poll an extra time
157
+ let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 2, 2, 2], mock);
158
+ mh.enforce_correct_number_of_polls = false;
159
+ let mut mock = build_mock_pollers(mh);
160
+ mock.worker_cfg(TEST_Q, |wc| wc.max_cached_workflows = 1);
161
+ let core = Arc::new(mock_core(mock));
162
+ let mut worker = TestRustWorker::new(core.clone(), TEST_Q.to_string(), None);
163
+ let shutdown_barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
164
+
165
+ worker.register_wf(
166
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
167
+ |ctx: WfContext| async move {
168
+ ctx.local_activity(LocalActivityOptions {
169
+ activity_type: "echo".to_string(),
170
+ input: "hi".as_json_payload().expect("serializes fine"),
171
+ ..Default::default()
172
+ })
173
+ .await;
174
+ Ok(().into())
175
+ },
176
+ );
177
+ worker.register_activity("echo", move |str: String| async move {
178
+ if shutdown_middle {
179
+ shutdown_barr.wait().await;
180
+ }
181
+ // Take slightly more than two workflow tasks
182
+ tokio::time::sleep(wft_timeout.mul_f32(2.2)).await;
183
+ Ok(str)
184
+ });
185
+ worker
186
+ .submit_wf(wf_id.to_owned(), DEFAULT_WORKFLOW_TYPE.to_owned(), vec![])
187
+ .await
188
+ .unwrap();
189
+ let (_, runres) = tokio::join!(
190
+ async {
191
+ if shutdown_middle {
192
+ shutdown_barr.wait().await;
193
+ core.shutdown().await;
194
+ }
195
+ },
196
+ worker.run_until_done()
197
+ );
198
+ runres.unwrap();
199
+ }
200
+
201
+ #[rstest::rstest]
202
+ #[case::retry_then_pass(true)]
203
+ #[case::retry_until_fail(false)]
204
+ #[tokio::test]
205
+ async fn local_act_fail_and_retry(#[case] eventually_pass: bool) {
206
+ let mut t = TestHistoryBuilder::default();
207
+ t.add_by_type(EventType::WorkflowExecutionStarted);
208
+ t.add_workflow_task_scheduled_and_started();
209
+
210
+ let wf_id = "fakeid";
211
+ let mock = mock_gateway();
212
+ let mh = MockPollCfg::from_resp_batches(wf_id, t, [1], mock);
213
+ let mock = build_mock_pollers(mh);
214
+ let core = mock_core(mock);
215
+ let mut worker = TestRustWorker::new(Arc::new(core), TEST_Q.to_string(), None);
216
+
217
+ worker.register_wf(
218
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
219
+ move |ctx: WfContext| async move {
220
+ let la_res = ctx
221
+ .local_activity(LocalActivityOptions {
222
+ activity_type: "echo".to_string(),
223
+ input: "hi".as_json_payload().expect("serializes fine"),
224
+ retry_policy: RetryPolicy {
225
+ initial_interval: Some(Duration::from_millis(50).into()),
226
+ backoff_coefficient: 1.2,
227
+ maximum_interval: None,
228
+ maximum_attempts: 5,
229
+ non_retryable_error_types: vec![],
230
+ },
231
+ ..Default::default()
232
+ })
233
+ .await;
234
+ if eventually_pass {
235
+ assert!(la_res.completed_ok())
236
+ } else {
237
+ assert!(la_res.failed())
238
+ }
239
+ Ok(().into())
240
+ },
241
+ );
242
+ let attempts: &'static _ = Box::leak(Box::new(AtomicUsize::new(0)));
243
+ worker.register_activity("echo", move |_: String| async move {
244
+ // Succeed on 3rd attempt (which is ==2 since fetch_add returns prev val)
245
+ if 2 == attempts.fetch_add(1, Ordering::Relaxed) && eventually_pass {
246
+ Ok(())
247
+ } else {
248
+ Err(anyhow!("Oh no I failed!"))
249
+ }
250
+ });
251
+ worker
252
+ .submit_wf(wf_id.to_owned(), DEFAULT_WORKFLOW_TYPE.to_owned(), vec![])
253
+ .await
254
+ .unwrap();
255
+ worker.run_until_done().await.unwrap();
256
+ let expected_attempts = if eventually_pass { 3 } else { 5 };
257
+ assert_eq!(expected_attempts, attempts.load(Ordering::Relaxed));
258
+ }
259
+
260
+ #[tokio::test]
261
+ async fn local_act_retry_long_backoff_uses_timer() {
262
+ let mut t = TestHistoryBuilder::default();
263
+ t.add_by_type(EventType::WorkflowExecutionStarted);
264
+ t.add_full_wf_task();
265
+ t.add_local_activity_fail_marker(
266
+ 1,
267
+ "1",
268
+ Failure::application_failure("la failed".to_string(), false),
269
+ );
270
+ let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
271
+ t.add_timer_fired(timer_started_event_id, "1".to_string());
272
+ t.add_full_wf_task();
273
+ t.add_local_activity_fail_marker(
274
+ 2,
275
+ "2",
276
+ Failure::application_failure("la failed".to_string(), false),
277
+ );
278
+ let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
279
+ t.add_timer_fired(timer_started_event_id, "2".to_string());
280
+ t.add_full_wf_task();
281
+ t.add_workflow_execution_completed();
282
+
283
+ let wf_id = "fakeid";
284
+ let mock = mock_gateway();
285
+ let mh = MockPollCfg::from_resp_batches(
286
+ wf_id,
287
+ t,
288
+ [1.into(), 2.into(), ResponseType::AllHistory],
289
+ mock,
290
+ );
291
+ let mut mock = build_mock_pollers(mh);
292
+ mock.worker_cfg(TEST_Q, |w| w.max_cached_workflows = 1);
293
+ let core = mock_core(mock);
294
+ let mut worker = TestRustWorker::new(Arc::new(core), TEST_Q.to_string(), None);
295
+
296
+ worker.register_wf(
297
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
298
+ |ctx: WfContext| async move {
299
+ let la_res = ctx
300
+ .local_activity(LocalActivityOptions {
301
+ activity_type: "echo".to_string(),
302
+ input: "hi".as_json_payload().expect("serializes fine"),
303
+ retry_policy: RetryPolicy {
304
+ initial_interval: Some(Duration::from_millis(65).into()),
305
+ // This will make the second backoff 65 seconds, plenty to use timer
306
+ backoff_coefficient: 1_000.,
307
+ maximum_interval: Some(Duration::from_secs(600).into()),
308
+ maximum_attempts: 3,
309
+ non_retryable_error_types: vec![],
310
+ },
311
+ ..Default::default()
312
+ })
313
+ .await;
314
+ assert!(la_res.failed());
315
+ // Extra timer just to have an extra workflow task which we can return full history for
316
+ ctx.timer(Duration::from_secs(1)).await;
317
+ Ok(().into())
318
+ },
319
+ );
320
+ worker.register_activity("echo", move |_: String| async move {
321
+ Result::<(), _>::Err(anyhow!("Oh no I failed!"))
322
+ });
323
+ worker
324
+ .submit_wf(wf_id.to_owned(), DEFAULT_WORKFLOW_TYPE.to_owned(), vec![])
325
+ .await
326
+ .unwrap();
327
+ worker.run_until_done().await.unwrap();
328
+ }
@@ -1,25 +1,23 @@
1
1
  mod activity_tasks;
2
2
  mod child_workflows;
3
3
  mod determinism;
4
+ mod local_activities;
4
5
  mod queries;
5
6
  mod replay_flag;
6
- mod retry;
7
7
  mod workers;
8
8
  mod workflow_cancels;
9
9
  mod workflow_tasks;
10
10
 
11
11
  use crate::{
12
12
  errors::{PollActivityError, PollWfError},
13
- pollers::MockManualGateway,
14
- test_help::{
15
- build_fake_core, canned_histories, fake_sg_opts, hist_to_poll_resp, ResponseType, TEST_Q,
16
- },
13
+ test_help::{build_fake_core, canned_histories, hist_to_poll_resp, ResponseType, TEST_Q},
17
14
  Core, CoreInitOptionsBuilder, CoreSDK, WorkerConfigBuilder,
18
15
  };
19
16
  use futures::FutureExt;
20
17
  use std::time::Duration;
18
+ use temporal_client::mocks::{fake_sg_opts, mock_manual_gateway};
21
19
  use temporal_sdk_core_protos::{
22
- coresdk::workflow_completion::WfActivationCompletion,
20
+ coresdk::workflow_completion::WorkflowActivationCompletion,
23
21
  temporal::api::workflowservice::v1::PollActivityTaskQueueResponse,
24
22
  };
25
23
  use tokio::{sync::Barrier, time::sleep};
@@ -30,7 +28,7 @@ async fn after_shutdown_server_is_not_polled() {
30
28
  let core = build_fake_core("fake_wf_id", t, &[1]);
31
29
  let res = core.poll_workflow_activation(TEST_Q).await.unwrap();
32
30
  assert_eq!(res.jobs.len(), 1);
33
- core.complete_workflow_activation(WfActivationCompletion::empty(TEST_Q, res.run_id))
31
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(TEST_Q, res.run_id))
34
32
  .await
35
33
  .unwrap();
36
34
  core.shutdown().await;
@@ -46,7 +44,7 @@ lazy_static::lazy_static! {
46
44
  }
47
45
  #[tokio::test]
48
46
  async fn shutdown_interrupts_both_polls() {
49
- let mut mock_gateway = MockManualGateway::new();
47
+ let mut mock_gateway = mock_manual_gateway();
50
48
  mock_gateway
51
49
  .expect_poll_activity_task()
52
50
  .times(1)
@@ -96,7 +94,6 @@ async fn shutdown_interrupts_both_polls() {
96
94
  .build()
97
95
  .unwrap(),
98
96
  )
99
- .await
100
97
  .unwrap();
101
98
  tokio::join! {
102
99
  async {
@@ -1,16 +1,20 @@
1
1
  use crate::{
2
- pollers::MockServerGatewayApis,
3
2
  test_help::{
4
3
  canned_histories, hist_to_poll_resp, mock_core, MocksHolder, ResponseType, TEST_Q,
5
4
  },
6
5
  Core,
7
6
  };
8
- use std::collections::{HashMap, VecDeque};
7
+ use std::{
8
+ collections::{HashMap, VecDeque},
9
+ time::Duration,
10
+ };
11
+ use temporal_client::mocks::mock_gateway;
12
+
9
13
  use temporal_sdk_core_protos::{
10
14
  coresdk::{
11
- workflow_activation::{wf_activation_job, WfActivationJob},
12
- workflow_commands::{CompleteWorkflowExecution, QueryResult, QuerySuccess, StartTimer},
13
- workflow_completion::WfActivationCompletion,
15
+ workflow_activation::{workflow_activation_job, WorkflowActivationJob},
16
+ workflow_commands::{CompleteWorkflowExecution, QueryResult, QuerySuccess},
17
+ workflow_completion::WorkflowActivationCompletion,
14
18
  },
15
19
  temporal::api::{
16
20
  failure::v1::Failure,
@@ -21,6 +25,7 @@ use temporal_sdk_core_protos::{
21
25
  },
22
26
  },
23
27
  };
28
+ use temporal_sdk_core_test_utils::start_timer_cmd;
24
29
 
25
30
  #[rstest::rstest]
26
31
  #[case::with_history(true)]
@@ -37,6 +42,7 @@ async fn legacy_query(#[case] include_history: bool) {
37
42
  pr.query = Some(WorkflowQuery {
38
43
  query_type: "query-type".to_string(),
39
44
  query_args: Some(b"hi".into()),
45
+ header: None,
40
46
  });
41
47
  if !include_history {
42
48
  pr.history = Some(History { events: vec![] });
@@ -45,7 +51,7 @@ async fn legacy_query(#[case] include_history: bool) {
45
51
  },
46
52
  hist_to_poll_resp(&t, wfid.to_owned(), 2.into(), TEST_Q.to_string()),
47
53
  ]);
48
- let mut mock_gateway = MockServerGatewayApis::new();
54
+ let mut mock_gateway = mock_gateway();
49
55
  mock_gateway
50
56
  .expect_complete_workflow_task()
51
57
  .returning(|_| Ok(RespondWorkflowTaskCompletedResponse::default()));
@@ -62,21 +68,17 @@ async fn legacy_query(#[case] include_history: bool) {
62
68
 
63
69
  let first_wft = || async {
64
70
  let task = core.poll_workflow_activation(TEST_Q).await.unwrap();
65
- core.complete_workflow_activation(WfActivationCompletion::from_cmd(
71
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
66
72
  TEST_Q,
67
73
  task.run_id,
68
- StartTimer {
69
- seq: 1,
70
- ..Default::default()
71
- }
72
- .into(),
74
+ start_timer_cmd(1, Duration::from_secs(1)),
73
75
  ))
74
76
  .await
75
77
  .unwrap();
76
78
  };
77
79
  let clear_eviction = || async {
78
80
  let t = core.poll_workflow_activation(TEST_Q).await.unwrap();
79
- core.complete_workflow_activation(WfActivationCompletion::empty(TEST_Q, t.run_id))
81
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(TEST_Q, t.run_id))
80
82
  .await
81
83
  .unwrap();
82
84
  };
@@ -92,12 +94,12 @@ async fn legacy_query(#[case] include_history: bool) {
92
94
  // Poll again, and we end up getting a `query` field query response
93
95
  let query = assert_matches!(
94
96
  task.jobs.as_slice(),
95
- [WfActivationJob {
96
- variant: Some(wf_activation_job::Variant::QueryWorkflow(q)),
97
+ [WorkflowActivationJob {
98
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(q)),
97
99
  }] => q
98
100
  );
99
101
  // Complete the query
100
- core.complete_workflow_activation(WfActivationCompletion::from_cmd(
102
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
101
103
  TEST_Q,
102
104
  task.run_id,
103
105
  QueryResult {
@@ -122,11 +124,11 @@ async fn legacy_query(#[case] include_history: bool) {
122
124
  let task = core.poll_workflow_activation(TEST_Q).await.unwrap();
123
125
  assert_matches!(
124
126
  task.jobs.as_slice(),
125
- [WfActivationJob {
126
- variant: Some(wf_activation_job::Variant::FireTimer(_)),
127
+ [WorkflowActivationJob {
128
+ variant: Some(workflow_activation_job::Variant::FireTimer(_)),
127
129
  }]
128
130
  );
129
- core.complete_workflow_activation(WfActivationCompletion::from_cmds(
131
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
130
132
  TEST_Q,
131
133
  task.run_id,
132
134
  vec![CompleteWorkflowExecution { result: None }.into()],
@@ -155,13 +157,14 @@ async fn new_queries(#[case] num_queries: usize) {
155
157
  WorkflowQuery {
156
158
  query_type: "query-type".to_string(),
157
159
  query_args: Some(b"hi".into()),
160
+ header: None,
158
161
  },
159
162
  );
160
163
  }
161
164
  pr
162
165
  },
163
166
  ]);
164
- let mut mock_gateway = MockServerGatewayApis::new();
167
+ let mut mock_gateway = mock_gateway();
165
168
  mock_gateway
166
169
  .expect_complete_workflow_task()
167
170
  .returning(|_| Ok(RespondWorkflowTaskCompletedResponse::default()));
@@ -172,14 +175,10 @@ async fn new_queries(#[case] num_queries: usize) {
172
175
  let core = mock_core(mock);
173
176
 
174
177
  let task = core.poll_workflow_activation(TEST_Q).await.unwrap();
175
- core.complete_workflow_activation(WfActivationCompletion::from_cmd(
178
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
176
179
  TEST_Q,
177
180
  task.run_id,
178
- StartTimer {
179
- seq: 1,
180
- ..Default::default()
181
- }
182
- .into(),
181
+ start_timer_cmd(1, Duration::from_secs(1)),
183
182
  ))
184
183
  .await
185
184
  .unwrap();
@@ -187,15 +186,15 @@ async fn new_queries(#[case] num_queries: usize) {
187
186
  let task = core.poll_workflow_activation(TEST_Q).await.unwrap();
188
187
  assert_matches!(
189
188
  task.jobs[0],
190
- WfActivationJob {
191
- variant: Some(wf_activation_job::Variant::FireTimer(_)),
189
+ WorkflowActivationJob {
190
+ variant: Some(workflow_activation_job::Variant::FireTimer(_)),
192
191
  }
193
192
  );
194
193
  for i in 1..=num_queries {
195
194
  assert_matches!(
196
195
  task.jobs[i],
197
- WfActivationJob {
198
- variant: Some(wf_activation_job::Variant::QueryWorkflow(_)),
196
+ WorkflowActivationJob {
197
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(_)),
199
198
  }
200
199
  );
201
200
  }
@@ -214,7 +213,7 @@ async fn new_queries(#[case] num_queries: usize) {
214
213
  })
215
214
  .collect();
216
215
  qresults.push(CompleteWorkflowExecution { result: None }.into());
217
- core.complete_workflow_activation(WfActivationCompletion::from_cmds(
216
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
218
217
  TEST_Q,
219
218
  task.run_id,
220
219
  qresults,
@@ -235,13 +234,14 @@ async fn legacy_query_failure_on_wft_failure() {
235
234
  pr.query = Some(WorkflowQuery {
236
235
  query_type: "query-type".to_string(),
237
236
  query_args: Some(b"hi".into()),
237
+ header: None,
238
238
  });
239
239
  pr.history = Some(History { events: vec![] });
240
240
  pr
241
241
  },
242
242
  hist_to_poll_resp(&t, wfid.to_owned(), 2.into(), TEST_Q.to_string()),
243
243
  ]);
244
- let mut mock_gateway = MockServerGatewayApis::new();
244
+ let mut mock_gateway = mock_gateway();
245
245
  mock_gateway
246
246
  .expect_complete_workflow_task()
247
247
  .returning(|_| Ok(RespondWorkflowTaskCompletedResponse::default()));
@@ -255,14 +255,10 @@ async fn legacy_query_failure_on_wft_failure() {
255
255
  let core = mock_core(mock);
256
256
 
257
257
  let task = core.poll_workflow_activation(TEST_Q).await.unwrap();
258
- core.complete_workflow_activation(WfActivationCompletion::from_cmd(
258
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
259
259
  TEST_Q,
260
260
  task.run_id,
261
- StartTimer {
262
- seq: 1,
263
- ..Default::default()
264
- }
265
- .into(),
261
+ start_timer_cmd(1, Duration::from_secs(1)),
266
262
  ))
267
263
  .await
268
264
  .unwrap();
@@ -271,12 +267,12 @@ async fn legacy_query_failure_on_wft_failure() {
271
267
  // Poll again, and we end up getting a `query` field query response
272
268
  assert_matches!(
273
269
  task.jobs.as_slice(),
274
- [WfActivationJob {
275
- variant: Some(wf_activation_job::Variant::QueryWorkflow(q)),
270
+ [WorkflowActivationJob {
271
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(q)),
276
272
  }] => q
277
273
  );
278
274
  // Fail wft which should result in query being failed
279
- core.complete_workflow_activation(WfActivationCompletion::fail(
275
+ core.complete_workflow_activation(WorkflowActivationCompletion::fail(
280
276
  TEST_Q,
281
277
  task.run_id,
282
278
  Failure {
@@ -290,10 +286,17 @@ async fn legacy_query_failure_on_wft_failure() {
290
286
  core.shutdown().await;
291
287
  }
292
288
 
289
+ #[rstest::rstest]
293
290
  #[tokio::test]
294
- async fn legacy_query_with_full_history_after_complete() {
291
+ async fn legacy_query_after_complete(#[values(false, true)] full_history: bool) {
295
292
  let wfid = "fake_wf_id";
296
- let t = canned_histories::single_timer_wf_completes("1");
293
+ let t = if full_history {
294
+ canned_histories::single_timer_wf_completes("1")
295
+ } else {
296
+ let mut t = canned_histories::single_timer("1");
297
+ t.add_workflow_task_completed();
298
+ t
299
+ };
297
300
  let query_with_hist_task = {
298
301
  let mut pr = hist_to_poll_resp(
299
302
  &t,
@@ -304,6 +307,7 @@ async fn legacy_query_with_full_history_after_complete() {
304
307
  pr.query = Some(WorkflowQuery {
305
308
  query_type: "query-type".to_string(),
306
309
  query_args: Some(b"hi".into()),
310
+ header: None,
307
311
  });
308
312
  pr
309
313
  };
@@ -317,7 +321,7 @@ async fn legacy_query_with_full_history_after_complete() {
317
321
  query_with_hist_task.clone(),
318
322
  query_with_hist_task,
319
323
  ]);
320
- let mut mock_gateway = MockServerGatewayApis::new();
324
+ let mut mock_gateway = mock_gateway();
321
325
  mock_gateway
322
326
  .expect_complete_workflow_task()
323
327
  .returning(|_| Ok(RespondWorkflowTaskCompletedResponse::default()));
@@ -331,19 +335,15 @@ async fn legacy_query_with_full_history_after_complete() {
331
335
  let core = mock_core(mock);
332
336
 
333
337
  let task = core.poll_workflow_activation(TEST_Q).await.unwrap();
334
- core.complete_workflow_activation(WfActivationCompletion::from_cmd(
338
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
335
339
  TEST_Q,
336
340
  task.run_id,
337
- StartTimer {
338
- seq: 1,
339
- ..Default::default()
340
- }
341
- .into(),
341
+ start_timer_cmd(1, Duration::from_secs(1)),
342
342
  ))
343
343
  .await
344
344
  .unwrap();
345
345
  let task = core.poll_workflow_activation(TEST_Q).await.unwrap();
346
- core.complete_workflow_activation(WfActivationCompletion::from_cmds(
346
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
347
347
  TEST_Q,
348
348
  task.run_id,
349
349
  vec![CompleteWorkflowExecution { result: None }.into()],
@@ -356,11 +356,11 @@ async fn legacy_query_with_full_history_after_complete() {
356
356
  let task = core.poll_workflow_activation(TEST_Q).await.unwrap();
357
357
  let query = assert_matches!(
358
358
  task.jobs.as_slice(),
359
- [WfActivationJob {
360
- variant: Some(wf_activation_job::Variant::QueryWorkflow(q)),
359
+ [WorkflowActivationJob {
360
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(q)),
361
361
  }] => q
362
362
  );
363
- core.complete_workflow_activation(WfActivationCompletion::from_cmd(
363
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
364
364
  TEST_Q,
365
365
  task.run_id,
366
366
  QueryResult {