temporalio 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -23
  3. data/bridge/Cargo.lock +168 -59
  4. data/bridge/Cargo.toml +4 -2
  5. data/bridge/sdk-core/README.md +19 -6
  6. data/bridge/sdk-core/client/src/lib.rs +215 -39
  7. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  8. data/bridge/sdk-core/client/src/raw.rs +4 -4
  9. data/bridge/sdk-core/client/src/retry.rs +32 -20
  10. data/bridge/sdk-core/core/Cargo.toml +22 -9
  11. data/bridge/sdk-core/core/src/abstractions.rs +203 -14
  12. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +76 -41
  13. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  14. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +204 -83
  15. data/bridge/sdk-core/core/src/core_tests/queries.rs +3 -4
  16. data/bridge/sdk-core/core/src/core_tests/workers.rs +1 -3
  17. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +397 -54
  18. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  19. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  20. data/bridge/sdk-core/core/src/lib.rs +16 -9
  21. data/bridge/sdk-core/core/src/telemetry/log_export.rs +1 -1
  22. data/bridge/sdk-core/core/src/telemetry/metrics.rs +69 -35
  23. data/bridge/sdk-core/core/src/telemetry/mod.rs +29 -13
  24. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +17 -12
  25. data/bridge/sdk-core/core/src/test_help/mod.rs +62 -12
  26. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  27. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  28. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +352 -122
  29. data/bridge/sdk-core/core/src/worker/activities.rs +233 -157
  30. data/bridge/sdk-core/core/src/worker/client/mocks.rs +22 -2
  31. data/bridge/sdk-core/core/src/worker/client.rs +18 -2
  32. data/bridge/sdk-core/core/src/worker/mod.rs +165 -58
  33. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  34. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  35. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +856 -277
  36. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +100 -43
  37. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +7 -7
  38. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +5 -4
  39. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +87 -27
  40. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +5 -4
  41. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +5 -4
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +5 -4
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +137 -62
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +25 -17
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +7 -6
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +103 -152
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +7 -7
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -7
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +5 -16
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +201 -121
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +11 -14
  54. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +30 -15
  55. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1026 -376
  56. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +460 -384
  57. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  58. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  60. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +448 -718
  63. data/bridge/sdk-core/core-api/Cargo.toml +2 -1
  64. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  65. data/bridge/sdk-core/core-api/src/lib.rs +6 -2
  66. data/bridge/sdk-core/core-api/src/telemetry.rs +0 -6
  67. data/bridge/sdk-core/core-api/src/worker.rs +14 -1
  68. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  69. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  70. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  71. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
  72. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
  73. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
  74. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
  75. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
  76. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
  77. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
  78. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
  79. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
  80. data/bridge/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
  81. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  82. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
  83. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
  84. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  85. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  86. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  87. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  88. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  89. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  90. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  91. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +7 -0
  92. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -0
  93. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  94. data/bridge/sdk-core/sdk/Cargo.toml +3 -2
  95. data/bridge/sdk-core/sdk/src/lib.rs +87 -20
  96. data/bridge/sdk-core/sdk/src/workflow_future.rs +9 -8
  97. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  98. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -1
  99. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +100 -87
  100. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +5 -1
  101. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +175 -57
  102. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  103. data/bridge/sdk-core/test-utils/Cargo.toml +3 -1
  104. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  105. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  106. data/bridge/sdk-core/test-utils/src/lib.rs +82 -23
  107. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  108. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  109. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  110. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  111. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  112. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
  113. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  114. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  115. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  116. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  117. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  118. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  119. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  120. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  121. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  122. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  123. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +72 -191
  124. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -0
  125. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  126. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
  127. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  128. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  129. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  130. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  131. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  132. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +10 -11
  133. data/bridge/sdk-core/tests/main.rs +3 -13
  134. data/bridge/sdk-core/tests/runner.rs +75 -36
  135. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  136. data/bridge/src/connection.rs +41 -25
  137. data/bridge/src/lib.rs +269 -14
  138. data/bridge/src/runtime.rs +1 -1
  139. data/bridge/src/test_server.rs +153 -0
  140. data/bridge/src/worker.rs +89 -16
  141. data/lib/gen/temporal/api/command/v1/message_pb.rb +4 -18
  142. data/lib/gen/temporal/api/common/v1/message_pb.rb +4 -0
  143. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +1 -3
  144. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -3
  145. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +2 -0
  146. data/lib/gen/temporal/api/enums/v1/update_pb.rb +6 -4
  147. data/lib/gen/temporal/api/history/v1/message_pb.rb +27 -19
  148. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -0
  149. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +3 -0
  150. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  151. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  152. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  153. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  154. data/lib/gen/temporal/api/update/v1/message_pb.rb +72 -0
  155. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +26 -16
  156. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  157. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  158. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  159. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  160. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  161. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  162. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +27 -21
  163. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +28 -24
  164. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  165. data/lib/temporalio/activity/context.rb +13 -8
  166. data/lib/temporalio/activity/info.rb +1 -1
  167. data/lib/temporalio/bridge/connect_options.rb +15 -0
  168. data/lib/temporalio/bridge/retry_config.rb +24 -0
  169. data/lib/temporalio/bridge/tls_options.rb +19 -0
  170. data/lib/temporalio/client/implementation.rb +8 -8
  171. data/lib/temporalio/connection/retry_config.rb +44 -0
  172. data/lib/temporalio/connection/service.rb +20 -0
  173. data/lib/temporalio/connection/test_service.rb +92 -0
  174. data/lib/temporalio/connection/tls_options.rb +51 -0
  175. data/lib/temporalio/connection/workflow_service.rb +731 -0
  176. data/lib/temporalio/connection.rb +55 -720
  177. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  178. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  179. data/lib/temporalio/interceptor/chain.rb +5 -5
  180. data/lib/temporalio/interceptor/client.rb +8 -4
  181. data/lib/temporalio/interceptor.rb +22 -0
  182. data/lib/temporalio/retry_policy.rb +13 -3
  183. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  184. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  185. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  186. data/lib/temporalio/testing.rb +175 -0
  187. data/lib/temporalio/version.rb +1 -1
  188. data/lib/temporalio/worker/activity_runner.rb +26 -4
  189. data/lib/temporalio/worker/activity_worker.rb +44 -18
  190. data/lib/temporalio/worker/sync_worker.rb +47 -11
  191. data/lib/temporalio/worker.rb +27 -21
  192. data/lib/temporalio/workflow/async.rb +46 -0
  193. data/lib/temporalio/workflow/future.rb +138 -0
  194. data/lib/temporalio/workflow/info.rb +76 -0
  195. data/temporalio.gemspec +4 -3
  196. metadata +67 -17
  197. data/bridge/sdk-core/Cargo.lock +0 -2606
  198. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
  199. data/lib/bridge.so +0 -0
  200. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +0 -25
  201. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +0 -49
  202. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
@@ -1,4 +1,5 @@
1
1
  use crate::{
2
+ internal_flags::CoreInternalFlags,
2
3
  replay::DEFAULT_WORKFLOW_TYPE,
3
4
  test_help::{canned_histories, mock_sdk, mock_sdk_cfg, MockPollCfg, ResponseType},
4
5
  worker::client::mocks::mock_workflow_client,
@@ -8,8 +9,13 @@ use std::{
8
9
  time::Duration,
9
10
  };
10
11
  use temporal_client::WorkflowOptions;
11
- use temporal_sdk::{WfContext, WorkflowResult};
12
- use temporal_sdk_core_protos::temporal::api::enums::v1::WorkflowTaskFailedCause;
12
+ use temporal_sdk::{
13
+ ActivityOptions, ChildWorkflowOptions, LocalActivityOptions, WfContext, WorkflowResult,
14
+ };
15
+ use temporal_sdk_core_protos::{
16
+ temporal::api::{enums::v1::WorkflowTaskFailedCause, failure::v1::Failure},
17
+ DEFAULT_ACTIVITY_TYPE,
18
+ };
13
19
 
14
20
  static DID_FAIL: AtomicBool = AtomicBool::new(false);
15
21
  pub async fn timer_wf_fails_once(ctx: WfContext) -> WorkflowResult<()> {
@@ -105,3 +111,160 @@ async fn test_wf_task_rejected_properly_due_to_nondeterminism(#[case] use_cache:
105
111
  // timer and proceed without restarting
106
112
  assert_eq!(2, started_count.load(Ordering::Relaxed));
107
113
  }
114
+
115
+ #[rstest::rstest]
116
+ #[tokio::test]
117
+ async fn activity_id_or_type_change_is_nondeterministic(
118
+ #[values(true, false)] use_cache: bool,
119
+ #[values(true, false)] id_change: bool,
120
+ #[values(true, false)] local_act: bool,
121
+ ) {
122
+ let wf_id = "fakeid";
123
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
124
+ let mut t = if local_act {
125
+ canned_histories::single_local_activity("1")
126
+ } else {
127
+ canned_histories::single_activity("1")
128
+ };
129
+ t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
130
+ let mock = mock_workflow_client();
131
+ let mut mh = MockPollCfg::from_resp_batches(
132
+ wf_id,
133
+ t,
134
+ // Two polls are needed, since the first will fail
135
+ [ResponseType::AllHistory, ResponseType::AllHistory],
136
+ mock,
137
+ );
138
+ // We should see one wft failure which has nondeterminism cause
139
+ mh.num_expected_fails = 1;
140
+ mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
141
+ let should_contain = if id_change {
142
+ "does not match activity id"
143
+ } else {
144
+ "does not match activity type"
145
+ };
146
+ matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
147
+ && matches!(f, Some(Failure {
148
+ message,
149
+ ..
150
+ }) if message.contains(should_contain))
151
+ });
152
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
153
+ if use_cache {
154
+ cfg.max_cached_workflows = 2;
155
+ }
156
+ });
157
+
158
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
159
+ if local_act {
160
+ ctx.local_activity(if id_change {
161
+ LocalActivityOptions {
162
+ activity_id: Some("I'm bad and wrong!".to_string()),
163
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
164
+ ..Default::default()
165
+ }
166
+ } else {
167
+ LocalActivityOptions {
168
+ activity_type: "not the default act type".to_string(),
169
+ ..Default::default()
170
+ }
171
+ })
172
+ .await;
173
+ } else {
174
+ ctx.activity(if id_change {
175
+ ActivityOptions {
176
+ activity_id: Some("I'm bad and wrong!".to_string()),
177
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
178
+ ..Default::default()
179
+ }
180
+ } else {
181
+ ActivityOptions {
182
+ activity_type: "not the default act type".to_string(),
183
+ ..Default::default()
184
+ }
185
+ })
186
+ .await;
187
+ }
188
+ Ok(().into())
189
+ });
190
+
191
+ worker
192
+ .submit_wf(
193
+ wf_id.to_owned(),
194
+ wf_type.to_owned(),
195
+ vec![],
196
+ WorkflowOptions::default(),
197
+ )
198
+ .await
199
+ .unwrap();
200
+ worker.run_until_done().await.unwrap();
201
+ }
202
+
203
+ #[rstest::rstest]
204
+ #[tokio::test]
205
+ async fn child_wf_id_or_type_change_is_nondeterministic(
206
+ #[values(true, false)] use_cache: bool,
207
+ #[values(true, false)] id_change: bool,
208
+ ) {
209
+ let wf_id = "fakeid";
210
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
211
+ let mut t = canned_histories::single_child_workflow("1");
212
+ t.set_flags_first_wft(&[CoreInternalFlags::IdAndTypeDeterminismChecks as u32], &[]);
213
+ let mock = mock_workflow_client();
214
+ let mut mh = MockPollCfg::from_resp_batches(
215
+ wf_id,
216
+ t,
217
+ // Two polls are needed, since the first will fail
218
+ [ResponseType::AllHistory, ResponseType::AllHistory],
219
+ mock,
220
+ );
221
+ // We should see one wft failure which has nondeterminism cause
222
+ mh.num_expected_fails = 1;
223
+ mh.expect_fail_wft_matcher = Box::new(move |_, cause, f| {
224
+ let should_contain = if id_change {
225
+ "does not match child workflow id"
226
+ } else {
227
+ "does not match child workflow type"
228
+ };
229
+ matches!(cause, WorkflowTaskFailedCause::NonDeterministicError)
230
+ && matches!(f, Some(Failure {
231
+ message,
232
+ ..
233
+ }) if message.contains(should_contain))
234
+ });
235
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
236
+ if use_cache {
237
+ cfg.max_cached_workflows = 2;
238
+ }
239
+ });
240
+
241
+ worker.register_wf(wf_type.to_owned(), move |ctx: WfContext| async move {
242
+ ctx.child_workflow(if id_change {
243
+ ChildWorkflowOptions {
244
+ workflow_id: "I'm bad and wrong!".to_string(),
245
+ workflow_type: DEFAULT_ACTIVITY_TYPE.to_string(),
246
+ ..Default::default()
247
+ }
248
+ } else {
249
+ ChildWorkflowOptions {
250
+ workflow_id: "1".to_string(),
251
+ workflow_type: "not the child wf type".to_string(),
252
+ ..Default::default()
253
+ }
254
+ })
255
+ .start(&ctx)
256
+ .await;
257
+ Ok(().into())
258
+ });
259
+
260
+ worker
261
+ .submit_wf(
262
+ wf_id.to_owned(),
263
+ wf_type.to_owned(),
264
+ vec![],
265
+ WorkflowOptions::default(),
266
+ )
267
+ .await
268
+ .unwrap();
269
+ worker.run_until_done().await.unwrap();
270
+ }
@@ -8,6 +8,7 @@ use crate::{
8
8
  worker::{client::mocks::mock_workflow_client, LEGACY_QUERY_ID},
9
9
  };
10
10
  use anyhow::anyhow;
11
+ use crossbeam::queue::SegQueue;
11
12
  use futures::{future::join_all, FutureExt};
12
13
  use std::{
13
14
  collections::HashMap,
@@ -19,7 +20,9 @@ use std::{
19
20
  time::{Duration, SystemTime},
20
21
  };
21
22
  use temporal_client::WorkflowOptions;
22
- use temporal_sdk::{ActContext, LocalActivityOptions, WfContext, WorkflowResult};
23
+ use temporal_sdk::{
24
+ ActContext, ActivityCancelledError, LocalActivityOptions, WfContext, WorkflowResult,
25
+ };
23
26
  use temporal_sdk_core_api::Worker;
24
27
  use temporal_sdk_core_protos::{
25
28
  coresdk::{
@@ -31,11 +34,11 @@ use temporal_sdk_core_protos::{
31
34
  },
32
35
  temporal::api::{
33
36
  common::v1::RetryPolicy,
34
- enums::v1::{EventType, TimeoutType},
37
+ enums::v1::{EventType, TimeoutType, WorkflowTaskFailedCause},
35
38
  failure::v1::Failure,
36
- history::v1::History,
37
39
  query::v1::WorkflowQuery,
38
40
  },
41
+ DEFAULT_ACTIVITY_TYPE,
39
42
  };
40
43
  use temporal_sdk_core_test_utils::{
41
44
  schedule_local_activity_cmd, start_timer_cmd, WorkerTestHelpers,
@@ -58,7 +61,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
58
61
  let mut t = TestHistoryBuilder::default();
59
62
  t.add_by_type(EventType::WorkflowExecutionStarted);
60
63
  t.add_full_wf_task();
61
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
64
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
62
65
  t.add_full_wf_task();
63
66
  t.add_local_activity_result_marker(1, "1", b"echo".into());
64
67
  t.add_timer_fired(timer_started_event_id, "1".to_string());
@@ -83,7 +86,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
83
86
  DEFAULT_WORKFLOW_TYPE.to_owned(),
84
87
  |ctx: WfContext| async move {
85
88
  let la = ctx.local_activity(LocalActivityOptions {
86
- activity_type: "echo".to_string(),
89
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
87
90
  input: "hi".as_json_payload().expect("serializes fine"),
88
91
  ..Default::default()
89
92
  });
@@ -92,7 +95,7 @@ async fn local_act_two_wfts_before_marker(#[case] replay: bool, #[case] cached:
92
95
  Ok(().into())
93
96
  },
94
97
  );
95
- worker.register_activity("echo", echo);
98
+ worker.register_activity(DEFAULT_ACTIVITY_TYPE, echo);
96
99
  worker
97
100
  .submit_wf(
98
101
  wf_id.to_owned(),
@@ -110,7 +113,7 @@ pub async fn local_act_fanout_wf(ctx: WfContext) -> WorkflowResult<()> {
110
113
  .map(|i| {
111
114
  ctx.local_activity(LocalActivityOptions {
112
115
  activity_type: "echo".to_string(),
113
- input: format!("Hi {}", i)
116
+ input: format!("Hi {i}")
114
117
  .as_json_payload()
115
118
  .expect("serializes fine"),
116
119
  ..Default::default()
@@ -127,7 +130,7 @@ async fn local_act_many_concurrent() {
127
130
  let mut t = TestHistoryBuilder::default();
128
131
  t.add_by_type(EventType::WorkflowExecutionStarted);
129
132
  t.add_full_wf_task();
130
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
133
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
131
134
  t.add_full_wf_task();
132
135
  for i in 1..=50 {
133
136
  t.add_local_activity_result_marker(i, &i.to_string(), b"echo".into());
@@ -168,12 +171,7 @@ async fn local_act_many_concurrent() {
168
171
  async fn local_act_heartbeat(#[case] shutdown_middle: bool) {
169
172
  let mut t = TestHistoryBuilder::default();
170
173
  let wft_timeout = Duration::from_millis(200);
171
- let mut wes_short_wft_timeout = default_wes_attribs();
172
- wes_short_wft_timeout.workflow_task_timeout = Some(wft_timeout.try_into().unwrap());
173
- t.add(
174
- EventType::WorkflowExecutionStarted,
175
- wes_short_wft_timeout.into(),
176
- );
174
+ t.add_wfe_started_with_wft_timeout(wft_timeout);
177
175
  t.add_full_wf_task();
178
176
  // Task created by WFT heartbeat
179
177
  t.add_full_wf_task();
@@ -304,7 +302,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
304
302
  "1",
305
303
  Failure::application_failure("la failed".to_string(), false),
306
304
  );
307
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
305
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
308
306
  t.add_timer_fired(timer_started_event_id, "1".to_string());
309
307
  t.add_full_wf_task();
310
308
  t.add_local_activity_fail_marker(
@@ -312,7 +310,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
312
310
  "2",
313
311
  Failure::application_failure("la failed".to_string(), false),
314
312
  );
315
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
313
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
316
314
  t.add_timer_fired(timer_started_event_id, "2".to_string());
317
315
  t.add_full_wf_task();
318
316
  t.add_workflow_execution_completed();
@@ -332,7 +330,7 @@ async fn local_act_retry_long_backoff_uses_timer() {
332
330
  |ctx: WfContext| async move {
333
331
  let la_res = ctx
334
332
  .local_activity(LocalActivityOptions {
335
- activity_type: "echo".to_string(),
333
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
336
334
  input: "hi".as_json_payload().expect("serializes fine"),
337
335
  retry_policy: RetryPolicy {
338
336
  initial_interval: Some(prost_dur!(from_millis(65))),
@@ -351,9 +349,12 @@ async fn local_act_retry_long_backoff_uses_timer() {
351
349
  Ok(().into())
352
350
  },
353
351
  );
354
- worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
355
- Result::<(), _>::Err(anyhow!("Oh no I failed!"))
356
- });
352
+ worker.register_activity(
353
+ DEFAULT_ACTIVITY_TYPE,
354
+ move |_ctx: ActContext, _: String| async move {
355
+ Result::<(), _>::Err(anyhow!("Oh no I failed!"))
356
+ },
357
+ );
357
358
  worker
358
359
  .submit_wf(
359
360
  wf_id.to_owned(),
@@ -413,7 +414,7 @@ async fn local_act_command_immediately_follows_la_marker() {
413
414
  t.add_full_wf_task();
414
415
  t.add_full_wf_task();
415
416
  t.add_local_activity_result_marker(1, "1", "done".into());
416
- t.add_get_event_id(EventType::TimerStarted, None);
417
+ t.add_by_type(EventType::TimerStarted);
417
418
  t.add_full_wf_task();
418
419
 
419
420
  let wf_id = "fakeid";
@@ -452,12 +453,7 @@ async fn local_act_command_immediately_follows_la_marker() {
452
453
  async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbeat() {
453
454
  let wfid = "fake_wf_id";
454
455
  let mut t = TestHistoryBuilder::default();
455
- let mut wes_short_wft_timeout = default_wes_attribs();
456
- wes_short_wft_timeout.workflow_task_timeout = Some(prost_dur!(from_millis(200)));
457
- t.add(
458
- EventType::WorkflowExecutionStarted,
459
- wes_short_wft_timeout.into(),
460
- );
456
+ t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
461
457
  t.add_full_wf_task();
462
458
  // get query here
463
459
  t.add_full_wf_task();
@@ -506,7 +502,7 @@ async fn query_during_wft_heartbeat_doesnt_accidentally_fail_to_continue_heartbe
506
502
  task.run_id,
507
503
  schedule_local_activity_cmd(
508
504
  1,
509
- "act-id",
505
+ "1",
510
506
  ActivityCancellationType::TryCancel,
511
507
  Duration::from_secs(60),
512
508
  ),
@@ -572,15 +568,11 @@ async fn la_resolve_during_legacy_query_does_not_combine(#[case] impossible_quer
572
568
  // never happen, but there was an issue where an LA resolving could trigger that.
573
569
  let wfid = "fake_wf_id";
574
570
  let mut t = TestHistoryBuilder::default();
575
- let wes_short_wft_timeout = default_wes_attribs();
576
- t.add(
577
- EventType::WorkflowExecutionStarted,
578
- wes_short_wft_timeout.into(),
579
- );
571
+ t.add(default_wes_attribs());
580
572
  // Since we don't send queries with start workflow, need one workflow task of something else
581
573
  // b/c we want to get an activation with a job and a nonlegacy query
582
574
  t.add_full_wf_task();
583
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
575
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
584
576
  t.add_timer_fired(timer_started_event_id, "1".to_string());
585
577
 
586
578
  // nonlegacy query got here & LA started here
@@ -622,8 +614,12 @@ async fn la_resolve_during_legacy_query_does_not_combine(#[case] impossible_quer
622
614
  2,
623
615
  ),
624
616
  );
625
- // Strip history, we need to look like we hit the cache
626
- pr.history = Some(History { events: vec![] });
617
+ // Strip beginning of history so the only events are WFT sched/started, we need to look
618
+ // like we hit the cache
619
+ {
620
+ let h = pr.history.as_mut().unwrap();
621
+ h.events = h.events.split_off(6);
622
+ }
627
623
  // In the nonsense server response case, we attach a legacy query, otherwise this
628
624
  // response looks like a normal response to a forced WFT heartbeat.
629
625
  if impossible_query_in_task {
@@ -783,12 +779,19 @@ async fn test_schedule_to_start_timeout() {
783
779
  worker.run_until_done().await.unwrap();
784
780
  }
785
781
 
782
+ #[rstest::rstest]
783
+ #[case::sched_to_start(true)]
784
+ #[case::sched_to_close(false)]
786
785
  #[tokio::test]
787
- async fn test_schedule_to_start_timeout_not_based_on_original_time() {
786
+ async fn test_schedule_to_start_timeout_not_based_on_original_time(
787
+ #[case] is_sched_to_start: bool,
788
+ ) {
788
789
  // We used to carry over the schedule time of LAs from the "original" schedule time if these LAs
789
790
  // created newly after backing off across a timer. That was a mistake, since schedule-to-start
790
- // timeouts should apply to when the new attempt was scheduled. This test verifies we don't
791
- // time out on s-t-s timeouts because of that.
791
+ // timeouts should apply to when the new attempt was scheduled. This test verifies:
792
+ // * we don't time out on s-t-s timeouts because of that, when the param is true.
793
+ // * we do properly time out on s-t-c timeouts when the param is false
794
+
792
795
  let mut t = TestHistoryBuilder::default();
793
796
  t.add_by_type(EventType::WorkflowExecutionStarted);
794
797
  t.add_full_wf_task();
@@ -805,7 +808,7 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
805
808
  deets.backoff = Some(prost_dur!(from_secs(100)));
806
809
  },
807
810
  );
808
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
811
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
809
812
  t.add_timer_fired(timer_started_event_id, "1".to_string());
810
813
  t.add_workflow_task_scheduled_and_started();
811
814
 
@@ -814,9 +817,18 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
814
817
  let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
815
818
  let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
816
819
 
820
+ let schedule_to_close_timeout = Some(if is_sched_to_start {
821
+ // This 60 minute timeout will not have elapsed according to the original
822
+ // schedule time in the history.
823
+ Duration::from_secs(60 * 60)
824
+ } else {
825
+ // This 10 minute timeout will have already elapsed
826
+ Duration::from_secs(10 * 60)
827
+ });
828
+
817
829
  worker.register_wf(
818
830
  DEFAULT_WORKFLOW_TYPE.to_owned(),
819
- |ctx: WfContext| async move {
831
+ move |ctx: WfContext| async move {
820
832
  let la_res = ctx
821
833
  .local_activity(LocalActivityOptions {
822
834
  activity_type: "echo".to_string(),
@@ -829,11 +841,15 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
829
841
  non_retryable_error_types: vec![],
830
842
  },
831
843
  schedule_to_start_timeout: Some(Duration::from_secs(60)),
832
- schedule_to_close_timeout: Some(Duration::from_secs(60 * 60)),
844
+ schedule_to_close_timeout,
833
845
  ..Default::default()
834
846
  })
835
847
  .await;
836
- assert!(la_res.completed_ok());
848
+ if is_sched_to_start {
849
+ assert!(la_res.completed_ok());
850
+ } else {
851
+ assert_eq!(la_res.timed_out(), Some(TimeoutType::ScheduleToClose));
852
+ }
837
853
  Ok(().into())
838
854
  },
839
855
  );
@@ -854,64 +870,162 @@ async fn test_schedule_to_start_timeout_not_based_on_original_time() {
854
870
  }
855
871
 
856
872
  #[tokio::test]
857
- async fn test_schedule_to_close_timeout_based_on_original_time() {
858
- // We used to carry over the schedule time of LAs from the "original" schedule time if these LAs
859
- // created newly after backing off across a timer. That was a mistake, since schedule-to-start
860
- // timeouts should apply to when the new attempt was scheduled. This test verifies we don't
861
- // time out on s-t-s timeouts because of that.
873
+ async fn wft_failure_cancels_running_las() {
862
874
  let mut t = TestHistoryBuilder::default();
863
- t.add_by_type(EventType::WorkflowExecutionStarted);
875
+ t.add_wfe_started_with_wft_timeout(Duration::from_millis(200));
864
876
  t.add_full_wf_task();
865
- let orig_sched = SystemTime::now().sub(Duration::from_secs(60 * 20));
866
- t.add_local_activity_marker(
867
- 1,
868
- "1",
869
- None,
870
- Some(Failure::application_failure("la failed".to_string(), false)),
871
- |deets| {
872
- // Really old schedule time, which should _not_ count against schedule_to_start
873
- deets.original_schedule_time = Some(orig_sched.into());
874
- // Backoff value must be present since we're simulating timer backoff
875
- deets.backoff = Some(prost_dur!(from_secs(100)));
876
- },
877
- );
878
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
877
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
879
878
  t.add_timer_fired(timer_started_event_id, "1".to_string());
880
879
  t.add_workflow_task_scheduled_and_started();
881
880
 
882
881
  let wf_id = "fakeid";
883
882
  let mock = mock_workflow_client();
884
- let mh = MockPollCfg::from_resp_batches(wf_id, t, [ResponseType::AllHistory], mock);
883
+ let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2], mock);
884
+ mh.num_expected_fails = 1;
885
885
  let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
886
886
 
887
887
  worker.register_wf(
888
888
  DEFAULT_WORKFLOW_TYPE.to_owned(),
889
889
  |ctx: WfContext| async move {
890
- let la_res = ctx
891
- .local_activity(LocalActivityOptions {
892
- activity_type: "echo".to_string(),
893
- input: "hi".as_json_payload().expect("serializes fine"),
894
- retry_policy: RetryPolicy {
895
- initial_interval: Some(prost_dur!(from_millis(50))),
896
- backoff_coefficient: 1.2,
897
- maximum_interval: None,
898
- maximum_attempts: 5,
899
- non_retryable_error_types: vec![],
900
- },
901
- // This 10 minute timeout will have already elapsed according to the original
902
- // schedule time in the history.
903
- schedule_to_close_timeout: Some(Duration::from_secs(10 * 60)),
904
- ..Default::default()
905
- })
906
- .await;
907
- assert_eq!(la_res.timed_out(), Some(TimeoutType::ScheduleToClose));
890
+ let la_handle = ctx.local_activity(LocalActivityOptions {
891
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
892
+ input: "hi".as_json_payload().expect("serializes fine"),
893
+ ..Default::default()
894
+ });
895
+ tokio::join!(
896
+ async {
897
+ ctx.timer(Duration::from_secs(1)).await;
898
+ panic!("ahhh I'm failing wft")
899
+ },
900
+ la_handle
901
+ );
908
902
  Ok(().into())
909
903
  },
910
904
  );
905
+ worker.register_activity(
906
+ DEFAULT_ACTIVITY_TYPE,
907
+ move |ctx: ActContext, _: String| async move {
908
+ let res = tokio::time::timeout(Duration::from_millis(500), ctx.cancelled()).await;
909
+ if res.is_err() {
910
+ panic!("Activity must be cancelled!!!!");
911
+ }
912
+ Result::<(), _>::Err(ActivityCancelledError::default().into())
913
+ },
914
+ );
915
+ worker
916
+ .submit_wf(
917
+ wf_id.to_owned(),
918
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
919
+ vec![],
920
+ WorkflowOptions::default(),
921
+ )
922
+ .await
923
+ .unwrap();
924
+ worker.run_until_done().await.unwrap();
925
+ }
926
+
927
+ #[tokio::test]
928
+ async fn resolved_las_not_recorded_if_wft_fails_many_times() {
929
+ // We shouldn't record any LA results if the workflow activation is repeatedly failing. There
930
+ // was an issue that, because we stop reporting WFT failures after 2 tries, this meant the WFT
931
+ // was not marked as "completed" and the WFT could accidentally be replied to with LA results.
932
+ let mut t = TestHistoryBuilder::default();
933
+ t.add_by_type(EventType::WorkflowExecutionStarted);
934
+ t.add_workflow_task_scheduled_and_started();
935
+ t.add_workflow_task_failed_with_failure(
936
+ WorkflowTaskFailedCause::Unspecified,
937
+ Default::default(),
938
+ );
939
+ t.add_workflow_task_scheduled_and_started();
940
+
941
+ let wf_id = "fakeid";
942
+ let mock = mock_workflow_client();
943
+ let mut mh = MockPollCfg::from_resp_batches(
944
+ wf_id,
945
+ t,
946
+ [1.into(), ResponseType::AllHistory, ResponseType::AllHistory],
947
+ mock,
948
+ );
949
+ mh.num_expected_fails = 2;
950
+ mh.num_expected_completions = Some(0.into());
951
+ let mut worker = mock_sdk_cfg(mh, |w| w.max_cached_workflows = 1);
952
+
953
+ worker.register_wf(
954
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
955
+ |ctx: WfContext| async move {
956
+ ctx.local_activity(LocalActivityOptions {
957
+ activity_type: "echo".to_string(),
958
+ input: "hi".as_json_payload().expect("serializes fine"),
959
+ ..Default::default()
960
+ })
961
+ .await;
962
+ panic!("Oh nooooo")
963
+ },
964
+ );
911
965
  worker.register_activity(
912
966
  "echo",
913
- move |_ctx: ActContext, _: String| async move { Ok(()) },
967
+ move |_: ActContext, _: String| async move { Ok(()) },
968
+ );
969
+ worker
970
+ .submit_wf(
971
+ wf_id.to_owned(),
972
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
973
+ vec![],
974
+ WorkflowOptions::default(),
975
+ )
976
+ .await
977
+ .unwrap();
978
+ worker.run_until_done().await.unwrap();
979
+ }
980
+
981
+ #[tokio::test]
982
+ async fn local_act_records_nonfirst_attempts_ok() {
983
+ let mut t = TestHistoryBuilder::default();
984
+ let wft_timeout = Duration::from_millis(200);
985
+ t.add_wfe_started_with_wft_timeout(wft_timeout);
986
+ t.add_full_wf_task();
987
+ t.add_full_wf_task();
988
+ t.add_full_wf_task();
989
+ t.add_workflow_task_scheduled_and_started();
990
+
991
+ let wf_id = "fakeid";
992
+ let mock = mock_workflow_client();
993
+ let mut mh = MockPollCfg::from_resp_batches(wf_id, t, [1, 2, 3], mock);
994
+ let nonfirst_counts = Arc::new(SegQueue::new());
995
+ let nfc_c = nonfirst_counts.clone();
996
+ mh.completion_asserts = Some(Box::new(move |c| {
997
+ nfc_c.push(
998
+ c.metering_metadata
999
+ .nonfirst_local_activity_execution_attempts,
1000
+ );
1001
+ }));
1002
+ let mut worker = mock_sdk_cfg(mh, |wc| {
1003
+ wc.max_cached_workflows = 1;
1004
+ wc.max_outstanding_workflow_tasks = 1;
1005
+ });
1006
+
1007
+ worker.register_wf(
1008
+ DEFAULT_WORKFLOW_TYPE.to_owned(),
1009
+ |ctx: WfContext| async move {
1010
+ ctx.local_activity(LocalActivityOptions {
1011
+ activity_type: "echo".to_string(),
1012
+ input: "hi".as_json_payload().expect("serializes fine"),
1013
+ retry_policy: RetryPolicy {
1014
+ initial_interval: Some(prost_dur!(from_millis(10))),
1015
+ backoff_coefficient: 1.0,
1016
+ maximum_interval: None,
1017
+ maximum_attempts: 0,
1018
+ non_retryable_error_types: vec![],
1019
+ },
1020
+ ..Default::default()
1021
+ })
1022
+ .await;
1023
+ Ok(().into())
1024
+ },
914
1025
  );
1026
+ worker.register_activity("echo", move |_ctx: ActContext, _: String| async move {
1027
+ Result::<(), _>::Err(anyhow!("I fail"))
1028
+ });
915
1029
  worker
916
1030
  .submit_wf(
917
1031
  wf_id.to_owned(),
@@ -922,4 +1036,11 @@ async fn test_schedule_to_close_timeout_based_on_original_time() {
922
1036
  .await
923
1037
  .unwrap();
924
1038
  worker.run_until_done().await.unwrap();
1039
+ // 3 workflow tasks
1040
+ assert_eq!(nonfirst_counts.len(), 3);
1041
+ // First task's non-first count should, of course, be 0
1042
+ assert_eq!(nonfirst_counts.pop().unwrap(), 0);
1043
+ // Next two, some nonzero amount which could vary based on test load
1044
+ assert!(nonfirst_counts.pop().unwrap() > 0);
1045
+ assert!(nonfirst_counts.pop().unwrap() > 0);
925
1046
  }
@@ -162,7 +162,7 @@ async fn new_queries(#[case] num_queries: usize) {
162
162
  pr.queries = HashMap::new();
163
163
  for i in 1..=num_queries {
164
164
  pr.queries.insert(
165
- format!("q{}", i),
165
+ format!("q{i}"),
166
166
  WorkflowQuery {
167
167
  query_type: "query-type".to_string(),
168
168
  query_args: Some(b"hi".into()),
@@ -206,7 +206,7 @@ async fn new_queries(#[case] num_queries: usize) {
206
206
  let mut qresults: Vec<_> = (1..=num_queries)
207
207
  .map(|i| {
208
208
  QueryResult {
209
- query_id: format!("q{}", i),
209
+ query_id: format!("q{i}"),
210
210
  variant: Some(
211
211
  QuerySuccess {
212
212
  response: Some(query_resp.into()),
@@ -769,11 +769,10 @@ async fn legacy_query_combined_with_timer_fire_repro() {
769
769
  t.add_by_type(EventType::WorkflowExecutionStarted);
770
770
  t.add_full_wf_task();
771
771
  let scheduled_event_id = t.add_activity_task_scheduled("1");
772
- let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
772
+ let timer_started_event_id = t.add_by_type(EventType::TimerStarted);
773
773
  t.add_timer_fired(timer_started_event_id, "1".to_string());
774
774
  t.add_full_wf_task();
775
775
  t.add(
776
- EventType::ActivityTaskCancelRequested,
777
776
  history_event::Attributes::ActivityTaskCancelRequestedEventAttributes(
778
777
  ActivityTaskCancelRequestedEventAttributes {
779
778
  scheduled_event_id,