temporalio 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (232) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +175 -4
  3. data/bridge/Cargo.lock +44 -21
  4. data/bridge/Cargo.toml +1 -0
  5. data/bridge/sdk-core/Cargo.toml +1 -1
  6. data/bridge/sdk-core/README.md +1 -4
  7. data/bridge/sdk-core/client/Cargo.toml +1 -1
  8. data/bridge/sdk-core/client/src/lib.rs +12 -20
  9. data/bridge/sdk-core/client/src/raw.rs +9 -8
  10. data/bridge/sdk-core/client/src/retry.rs +100 -23
  11. data/bridge/sdk-core/core/Cargo.toml +7 -7
  12. data/bridge/sdk-core/core/benches/workflow_replay.rs +13 -10
  13. data/bridge/sdk-core/core/src/abstractions.rs +22 -22
  14. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +146 -43
  15. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +419 -9
  16. data/bridge/sdk-core/core/src/core_tests/queries.rs +247 -89
  17. data/bridge/sdk-core/core/src/core_tests/workers.rs +2 -2
  18. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  19. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +47 -27
  20. data/bridge/sdk-core/core/src/lib.rs +139 -32
  21. data/bridge/sdk-core/core/src/protosext/mod.rs +1 -1
  22. data/bridge/sdk-core/core/src/replay/mod.rs +185 -41
  23. data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
  24. data/bridge/sdk-core/core/src/telemetry/metrics.rs +184 -139
  25. data/bridge/sdk-core/core/src/telemetry/mod.rs +310 -315
  26. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +4 -3
  27. data/bridge/sdk-core/core/src/test_help/mod.rs +23 -9
  28. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +12 -6
  29. data/bridge/sdk-core/core/src/worker/activities.rs +40 -23
  30. data/bridge/sdk-core/core/src/worker/client/mocks.rs +1 -1
  31. data/bridge/sdk-core/core/src/worker/client.rs +30 -4
  32. data/bridge/sdk-core/core/src/worker/mod.rs +23 -19
  33. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +10 -19
  34. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +99 -25
  35. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +1 -5
  36. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -5
  37. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +1 -5
  38. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +3 -5
  39. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +1 -5
  40. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +2 -6
  41. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +1 -5
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +24 -22
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -38
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +178 -0
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +1 -5
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -5
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +1 -5
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +8 -2
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +1 -5
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +1 -1
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +233 -217
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +1 -6
  53. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +4 -4
  54. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +13 -5
  55. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +86 -29
  56. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +2 -2
  57. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +56 -11
  58. data/bridge/sdk-core/core-api/Cargo.toml +4 -3
  59. data/bridge/sdk-core/core-api/src/lib.rs +1 -43
  60. data/bridge/sdk-core/core-api/src/telemetry.rs +147 -0
  61. data/bridge/sdk-core/core-api/src/worker.rs +13 -0
  62. data/bridge/sdk-core/etc/deps.svg +115 -140
  63. data/bridge/sdk-core/etc/regen-depgraph.sh +5 -0
  64. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  65. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  66. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  67. data/bridge/sdk-core/protos/api_upstream/buf.yaml +0 -3
  68. data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
  69. data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
  70. data/bridge/sdk-core/protos/api_upstream/{temporal/api/update/v1/message.proto → build/tools.go} +6 -23
  71. data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
  72. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +12 -9
  73. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +20 -19
  74. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +2 -2
  75. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  76. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +4 -4
  77. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  78. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +5 -3
  79. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +23 -2
  80. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/{cluster.proto → interaction_type.proto} +10 -11
  81. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  82. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  83. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  84. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  85. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  86. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +2 -13
  87. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  88. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  89. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  90. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  91. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +26 -19
  92. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +87 -0
  93. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -2
  94. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +21 -61
  95. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -21
  96. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  97. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  98. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +110 -31
  99. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +4 -4
  100. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  101. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +3 -2
  102. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +60 -16
  103. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +17 -3
  104. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +2 -0
  105. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +8 -1
  106. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  107. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  108. data/bridge/sdk-core/sdk/Cargo.toml +2 -2
  109. data/bridge/sdk-core/sdk/src/interceptors.rs +36 -3
  110. data/bridge/sdk-core/sdk/src/lib.rs +7 -5
  111. data/bridge/sdk-core/sdk/src/workflow_context.rs +13 -2
  112. data/bridge/sdk-core/sdk/src/workflow_future.rs +3 -7
  113. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +1 -1
  114. data/bridge/sdk-core/sdk-core-protos/build.rs +0 -1
  115. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +65 -18
  116. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +22 -22
  117. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +104 -44
  118. data/bridge/sdk-core/test-utils/Cargo.toml +2 -1
  119. data/bridge/sdk-core/test-utils/src/lib.rs +81 -29
  120. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -2
  121. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +37 -0
  122. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +0 -13
  123. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +167 -13
  124. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +53 -0
  125. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +106 -20
  126. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +18 -8
  127. data/bridge/sdk-core/tests/main.rs +6 -4
  128. data/bridge/src/connection.rs +81 -62
  129. data/bridge/src/lib.rs +92 -33
  130. data/bridge/src/runtime.rs +9 -2
  131. data/bridge/src/worker.rs +53 -2
  132. data/lib/bridge.so +0 -0
  133. data/lib/gen/temporal/api/batch/v1/message_pb.rb +8 -6
  134. data/lib/gen/temporal/api/command/v1/message_pb.rb +17 -9
  135. data/lib/gen/temporal/api/common/v1/message_pb.rb +1 -1
  136. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +2 -1
  137. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +3 -1
  138. data/lib/gen/temporal/api/enums/v1/common_pb.rb +2 -1
  139. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -2
  140. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +7 -1
  141. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +25 -0
  142. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +1 -1
  143. data/lib/gen/temporal/api/enums/v1/query_pb.rb +1 -1
  144. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +1 -1
  145. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +1 -1
  146. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +1 -1
  147. data/lib/gen/temporal/api/enums/v1/update_pb.rb +1 -6
  148. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +1 -1
  149. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +1 -1
  150. data/lib/gen/temporal/api/failure/v1/message_pb.rb +1 -1
  151. data/lib/gen/temporal/api/filter/v1/message_pb.rb +1 -1
  152. data/lib/gen/temporal/api/history/v1/message_pb.rb +19 -18
  153. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +49 -0
  154. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -1
  155. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +11 -51
  156. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +1 -1
  157. data/lib/gen/temporal/api/query/v1/message_pb.rb +1 -1
  158. data/lib/gen/temporal/api/replication/v1/message_pb.rb +1 -1
  159. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +22 -1
  160. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +2 -2
  161. data/lib/gen/temporal/api/version/v1/message_pb.rb +1 -1
  162. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +2 -1
  163. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +27 -10
  164. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +1 -1
  165. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +1 -0
  166. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +5 -1
  167. data/lib/temporalio/activity/context.rb +97 -0
  168. data/lib/temporalio/activity/info.rb +67 -0
  169. data/lib/temporalio/activity.rb +85 -0
  170. data/lib/temporalio/bridge/error.rb +8 -0
  171. data/lib/temporalio/bridge.rb +14 -0
  172. data/lib/{temporal → temporalio}/client/implementation.rb +49 -48
  173. data/lib/{temporal → temporalio}/client/workflow_handle.rb +35 -35
  174. data/lib/{temporal → temporalio}/client.rb +19 -32
  175. data/lib/{temporal → temporalio}/connection.rb +238 -223
  176. data/lib/{temporal → temporalio}/data_converter.rb +76 -35
  177. data/lib/{temporal → temporalio}/error/failure.rb +6 -6
  178. data/lib/{temporal → temporalio}/error/workflow_failure.rb +4 -2
  179. data/lib/{temporal → temporalio}/errors.rb +19 -1
  180. data/lib/{temporal → temporalio}/failure_converter/base.rb +5 -5
  181. data/lib/{temporal → temporalio}/failure_converter/basic.rb +58 -52
  182. data/lib/temporalio/failure_converter.rb +7 -0
  183. data/lib/{temporal → temporalio}/interceptor/chain.rb +2 -1
  184. data/lib/{temporal → temporalio}/interceptor/client.rb +22 -1
  185. data/lib/{temporal → temporalio}/payload_codec/base.rb +5 -5
  186. data/lib/{temporal → temporalio}/payload_converter/base.rb +3 -3
  187. data/lib/{temporal → temporalio}/payload_converter/bytes.rb +4 -3
  188. data/lib/{temporal → temporalio}/payload_converter/composite.rb +7 -5
  189. data/lib/{temporal → temporalio}/payload_converter/encoding_base.rb +4 -4
  190. data/lib/{temporal → temporalio}/payload_converter/json.rb +4 -3
  191. data/lib/{temporal → temporalio}/payload_converter/nil.rb +4 -3
  192. data/lib/temporalio/payload_converter.rb +14 -0
  193. data/lib/{temporal → temporalio}/retry_policy.rb +4 -4
  194. data/lib/{temporal → temporalio}/retry_state.rb +1 -1
  195. data/lib/temporalio/runtime.rb +25 -0
  196. data/lib/{temporal → temporalio}/timeout_type.rb +2 -2
  197. data/lib/temporalio/version.rb +3 -0
  198. data/lib/temporalio/worker/activity_runner.rb +92 -0
  199. data/lib/temporalio/worker/activity_worker.rb +138 -0
  200. data/lib/temporalio/worker/reactor.rb +46 -0
  201. data/lib/temporalio/worker/runner.rb +63 -0
  202. data/lib/temporalio/worker/sync_worker.rb +88 -0
  203. data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
  204. data/lib/temporalio/worker.rb +198 -0
  205. data/lib/{temporal → temporalio}/workflow/execution_info.rb +4 -4
  206. data/lib/{temporal → temporalio}/workflow/execution_status.rb +1 -1
  207. data/lib/{temporal → temporalio}/workflow/id_reuse_policy.rb +6 -6
  208. data/lib/{temporal → temporalio}/workflow/query_reject_condition.rb +5 -5
  209. data/lib/temporalio.rb +12 -3
  210. data/temporalio.gemspec +7 -3
  211. metadata +79 -56
  212. data/bridge/sdk-core/bridge-ffi/Cargo.toml +0 -24
  213. data/bridge/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  214. data/bridge/sdk-core/bridge-ffi/build.rs +0 -25
  215. data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -249
  216. data/bridge/sdk-core/bridge-ffi/src/lib.rs +0 -825
  217. data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +0 -211
  218. data/bridge/sdk-core/core/src/log_export.rs +0 -62
  219. data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +0 -127
  220. data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +0 -71
  221. data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +0 -83
  222. data/bridge/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  223. data/bridge/sdk-core/sdk/src/conversions.rs +0 -8
  224. data/lib/gen/temporal/api/cluster/v1/message_pb.rb +0 -67
  225. data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +0 -26
  226. data/lib/gen/temporal/api/update/v1/message_pb.rb +0 -26
  227. data/lib/temporal/bridge.rb +0 -14
  228. data/lib/temporal/failure_converter.rb +0 -8
  229. data/lib/temporal/payload_converter.rb +0 -14
  230. data/lib/temporal/runtime.rb +0 -22
  231. data/lib/temporal/version.rb +0 -3
  232. data/lib/temporal.rb +0 -8
@@ -1,19 +1,27 @@
1
1
  use anyhow::anyhow;
2
2
  use futures::future::join_all;
3
- use std::time::Duration;
4
- use temporal_client::WorkflowOptions;
3
+ use futures_util::stream::{FuturesUnordered, StreamExt};
4
+ use std::{
5
+ sync::atomic::{AtomicU8, Ordering},
6
+ time::Duration,
7
+ };
8
+ use temporal_client::{WorkflowClientTrait, WorkflowOptions};
5
9
  use temporal_sdk::{
6
- interceptors::WorkerInterceptor, ActContext, ActivityCancelledError, CancellableFuture,
7
- LocalActivityOptions, WfContext, WorkflowResult,
10
+ interceptors::WorkerInterceptor, ActContext, ActivityCancelledError, ActivityOptions,
11
+ CancellableFuture, LocalActivityOptions, WfContext, WorkflowResult,
8
12
  };
13
+ use temporal_sdk_core::replay::HistoryForReplay;
9
14
  use temporal_sdk_core_protos::{
10
15
  coresdk::{
11
16
  workflow_commands::ActivityCancellationType,
12
17
  workflow_completion::WorkflowActivationCompletion, AsJsonPayloadExt,
13
18
  },
14
- temporal::api::common::v1::RetryPolicy,
19
+ temporal::api::{common::v1::RetryPolicy, enums::v1::TimeoutType},
20
+ TestHistoryBuilder,
21
+ };
22
+ use temporal_sdk_core_test_utils::{
23
+ history_from_proto_binary, init_integ_telem, replay_sdk_worker, CoreWfStarter,
15
24
  };
16
- use temporal_sdk_core_test_utils::CoreWfStarter;
17
25
  use tokio_util::sync::CancellationToken;
18
26
 
19
27
  pub async fn echo(_ctx: ActContext, e: String) -> anyhow::Result<String> {
@@ -403,6 +411,11 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
403
411
  } else {
404
412
  (None, Some(Duration::from_secs(2)))
405
413
  };
414
+ let timeout_type = if is_schedule {
415
+ TimeoutType::ScheduleToClose
416
+ } else {
417
+ TimeoutType::StartToClose
418
+ };
406
419
 
407
420
  worker.register_wf(wf_name.to_owned(), move |ctx: WfContext| async move {
408
421
  let res = ctx
@@ -422,7 +435,7 @@ async fn x_to_close_timeout(#[case] is_schedule: bool) {
422
435
  ..Default::default()
423
436
  })
424
437
  .await;
425
- assert!(res.timed_out());
438
+ assert_eq!(res.timed_out(), Some(timeout_type));
426
439
  Ok(().into())
427
440
  });
428
441
  worker.register_activity("echo", |ctx: ActContext, _: String| async move {
@@ -467,21 +480,23 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
467
480
  activity_type: "echo".to_string(),
468
481
  input: "hi".as_json_payload().expect("serializes fine"),
469
482
  retry_policy: RetryPolicy {
470
- initial_interval: Some(prost_dur!(from_micros(15))),
483
+ initial_interval: Some(prost_dur!(from_millis(15))),
471
484
  backoff_coefficient: 1_000.,
472
- maximum_interval: Some(prost_dur!(from_millis(1500))),
485
+ maximum_interval: Some(prost_dur!(from_millis(1000))),
473
486
  maximum_attempts: 40,
474
487
  non_retryable_error_types: vec![],
475
488
  },
476
- timer_backoff_threshold: Some(Duration::from_secs(1)),
477
- schedule_to_close_timeout: Some(Duration::from_secs(3)),
489
+ timer_backoff_threshold: Some(Duration::from_millis(500)),
490
+ schedule_to_close_timeout: Some(Duration::from_secs(2)),
478
491
  ..Default::default()
479
492
  })
480
493
  .await;
481
- assert!(res.timed_out());
494
+ assert_eq!(res.timed_out(), Some(TimeoutType::ScheduleToClose));
482
495
  Ok(().into())
483
496
  });
484
- worker.register_activity("echo", |_: ActContext, _: String| async {
497
+ let num_attempts: &'static _ = Box::leak(Box::new(AtomicU8::new(0)));
498
+ worker.register_activity("echo", move |_: ActContext, _: String| async {
499
+ num_attempts.fetch_add(1, Ordering::Relaxed);
485
500
  Result::<(), _>::Err(anyhow!("Oh no I failed!"))
486
501
  });
487
502
 
@@ -495,6 +510,9 @@ async fn schedule_to_close_timeout_across_timer_backoff(#[case] cached: bool) {
495
510
  .await
496
511
  .unwrap();
497
512
  worker.run_until_done().await.unwrap();
513
+ // 3 attempts b/c first backoff is very small, then the next 2 attempts take at least 2 seconds
514
+ // b/c of timer backoff.
515
+ assert_eq!(3, num_attempts.load(Ordering::Relaxed));
498
516
  }
499
517
 
500
518
  #[rstest::rstest]
@@ -632,3 +650,139 @@ async fn repro_nondeterminism_with_timer_bug() {
632
650
  .await
633
651
  .unwrap();
634
652
  }
653
+
654
+ async fn la_problem_workflow(ctx: WfContext) -> WorkflowResult<()> {
655
+ ctx.local_activity(LocalActivityOptions {
656
+ activity_type: "delay".to_string(),
657
+ input: "hi".as_json_payload().expect("serializes fine"),
658
+ retry_policy: RetryPolicy {
659
+ initial_interval: Some(prost_dur!(from_micros(15))),
660
+ backoff_coefficient: 1_000.,
661
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
662
+ maximum_attempts: 4,
663
+ non_retryable_error_types: vec![],
664
+ },
665
+ timer_backoff_threshold: Some(Duration::from_secs(1)),
666
+ ..Default::default()
667
+ })
668
+ .await;
669
+ ctx.activity(ActivityOptions {
670
+ activity_type: "delay".to_string(),
671
+ start_to_close_timeout: Some(Duration::from_secs(20)),
672
+ input: "hi!".as_json_payload().expect("serializes fine"),
673
+ ..Default::default()
674
+ })
675
+ .await;
676
+ Ok(().into())
677
+ }
678
+
679
+ // Expensive to run - worth enabling on a stress/regression pipeline.
680
+ #[ignore]
681
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
682
+ async fn evict_while_la_running_no_interference() {
683
+ let wf_name = "evict_while_la_running_no_interference";
684
+ let mut starter = CoreWfStarter::new(wf_name);
685
+ starter.max_local_at(20);
686
+ starter.max_cached_workflows(20);
687
+ let mut worker = starter.worker().await;
688
+
689
+ worker.register_wf(wf_name.to_owned(), la_problem_workflow);
690
+ worker.register_activity("delay", |_: ActContext, _: String| async {
691
+ tokio::time::sleep(Duration::from_secs(15)).await;
692
+ Ok(())
693
+ });
694
+
695
+ let client = starter.get_client().await;
696
+ let subfs = FuturesUnordered::new();
697
+ for i in 1..100 {
698
+ let wf_id = format!("{}-{}", wf_name, i);
699
+ let run_id = worker
700
+ .submit_wf(
701
+ &wf_id,
702
+ wf_name.to_owned(),
703
+ vec![],
704
+ WorkflowOptions::default(),
705
+ )
706
+ .await
707
+ .unwrap();
708
+ let cw = worker.core_worker.clone();
709
+ let client = client.clone();
710
+ subfs.push(async move {
711
+ // Evict the workflow
712
+ tokio::time::sleep(Duration::from_secs(1)).await;
713
+ cw.request_workflow_eviction(&run_id);
714
+ // Wake up workflow by sending signal
715
+ client
716
+ .signal_workflow_execution(
717
+ wf_id,
718
+ run_id.clone(),
719
+ "whaatever".to_string(),
720
+ None,
721
+ None,
722
+ )
723
+ .await
724
+ .unwrap();
725
+ });
726
+ }
727
+ let runf = async {
728
+ worker.run_until_done().await.unwrap();
729
+ };
730
+ tokio::join!(subfs.collect::<Vec<_>>(), runf);
731
+ }
732
+
733
+ #[rstest::rstest]
734
+ #[tokio::test]
735
+ async fn weird_la_nondeterminism_repro(#[values(true, false)] fix_hist: bool) {
736
+ init_integ_telem();
737
+ let mut hist = history_from_proto_binary(
738
+ "histories/evict_while_la_running_no_interference-85_history.bin",
739
+ )
740
+ .await
741
+ .unwrap();
742
+ if fix_hist {
743
+ // Replace broken ending with accurate ending
744
+ hist.events.truncate(20);
745
+ let mut thb = TestHistoryBuilder::from_history(hist.events);
746
+ thb.add_workflow_task_completed();
747
+ thb.add_workflow_execution_completed();
748
+ hist = thb.get_full_history_info().unwrap().into();
749
+ }
750
+
751
+ let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
752
+ worker.register_wf(
753
+ "evict_while_la_running_no_interference",
754
+ la_problem_workflow,
755
+ );
756
+ worker.register_activity("delay", |_: ActContext, _: String| async {
757
+ tokio::time::sleep(Duration::from_secs(15)).await;
758
+ Ok(())
759
+ });
760
+ worker.run().await.unwrap();
761
+ }
762
+
763
+ #[tokio::test]
764
+ async fn second_weird_la_nondeterminism_repro() {
765
+ init_integ_telem();
766
+ let mut hist = history_from_proto_binary(
767
+ "histories/evict_while_la_running_no_interference-23_history.bin",
768
+ )
769
+ .await
770
+ .unwrap();
771
+ // Chop off uninteresting ending
772
+ hist.events.truncate(24);
773
+ let mut thb = TestHistoryBuilder::from_history(hist.events);
774
+ // thb.add_workflow_task_completed();
775
+ thb.add_workflow_execution_completed();
776
+ hist = thb.get_full_history_info().unwrap().into();
777
+
778
+ let mut worker = replay_sdk_worker([HistoryForReplay::new(hist, "fake".to_owned())]);
779
+ worker.register_wf(
780
+ "evict_while_la_running_no_interference",
781
+ la_problem_workflow,
782
+ );
783
+ worker.register_activity("delay", |_: ActContext, _: String| async {
784
+ tokio::time::sleep(Duration::from_secs(15)).await;
785
+ Ok(())
786
+ });
787
+ worker.run().await.unwrap();
788
+ }
@@ -0,0 +1,53 @@
1
+ use temporal_client::WorkflowClientTrait;
2
+ use temporal_sdk::{WfContext, WorkflowResult};
3
+ use temporal_sdk_core_protos::coresdk::{AsJsonPayloadExt, FromJsonPayloadExt};
4
+ use temporal_sdk_core_test_utils::CoreWfStarter;
5
+ use uuid::Uuid;
6
+
7
+ static FIELD_A: &str = "cat_name";
8
+ static FIELD_B: &str = "cute_level";
9
+
10
+ async fn memo_upserter(ctx: WfContext) -> WorkflowResult<()> {
11
+ ctx.upsert_memo([
12
+ (FIELD_A.to_string(), "enchi".as_json_payload().unwrap()),
13
+ (FIELD_B.to_string(), 9001.as_json_payload().unwrap()),
14
+ ]);
15
+ Ok(().into())
16
+ }
17
+
18
+ #[tokio::test]
19
+ async fn sends_modify_wf_props() {
20
+ let wf_name = "can_upsert_memo";
21
+ let wf_id = Uuid::new_v4();
22
+ let mut starter = CoreWfStarter::new(wf_name);
23
+ let mut worker = starter.worker().await;
24
+
25
+ worker.register_wf(wf_name, memo_upserter);
26
+ let run_id = worker
27
+ .submit_wf(wf_id.to_string(), wf_name, vec![], Default::default())
28
+ .await
29
+ .unwrap();
30
+ worker.run_until_done().await.unwrap();
31
+
32
+ let memo = starter
33
+ .get_client()
34
+ .await
35
+ .describe_workflow_execution(wf_id.to_string(), Some(run_id))
36
+ .await
37
+ .unwrap()
38
+ .workflow_execution_info
39
+ .unwrap()
40
+ .memo
41
+ .unwrap()
42
+ .fields;
43
+ let catname = memo.get(FIELD_A).unwrap();
44
+ let cuteness = memo.get(FIELD_B).unwrap();
45
+ for payload in [catname, cuteness] {
46
+ assert_eq!(
47
+ &b"json/plain".to_vec(),
48
+ payload.metadata.get("encoding").unwrap()
49
+ );
50
+ }
51
+ assert_eq!("enchi", String::from_json_payload(catname).unwrap());
52
+ assert_eq!(9001, usize::from_json_payload(cuteness).unwrap());
53
+ }
@@ -1,6 +1,8 @@
1
1
  use assert_matches::assert_matches;
2
- use std::time::Duration;
3
- use temporal_sdk::{WfContext, Worker, WorkflowFunction};
2
+ use parking_lot::Mutex;
3
+ use std::{collections::HashSet, sync::Arc, time::Duration};
4
+ use temporal_sdk::{interceptors::WorkerInterceptor, WfContext, Worker, WorkflowFunction};
5
+ use temporal_sdk_core::replay::{HistoryFeeder, HistoryForReplay};
4
6
  use temporal_sdk_core_api::errors::{PollActivityError, PollWfError};
5
7
  use temporal_sdk_core_protos::{
6
8
  coresdk::{
@@ -8,20 +10,29 @@ use temporal_sdk_core_protos::{
8
10
  workflow_commands::{ScheduleActivity, StartTimer},
9
11
  workflow_completion::WorkflowActivationCompletion,
10
12
  },
11
- DEFAULT_WORKFLOW_TYPE,
13
+ TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE,
12
14
  };
13
15
  use temporal_sdk_core_test_utils::{
14
- canned_histories, history_from_proto_binary, init_core_replay_preloaded, WorkerTestHelpers,
16
+ canned_histories, history_from_proto_binary, init_core_replay_preloaded, replay_sdk_worker,
17
+ replay_sdk_worker_stream, WorkerTestHelpers,
15
18
  };
16
19
  use tokio::join;
17
20
 
21
+ fn test_hist_to_replay(t: TestHistoryBuilder) -> HistoryForReplay {
22
+ let hi = t.get_full_history_info().unwrap().into();
23
+ HistoryForReplay::new(hi, "fake".to_string())
24
+ }
25
+
18
26
  #[tokio::test]
19
27
  async fn timer_workflow_replay() {
20
- let (core, _) = init_core_replay_preloaded(
28
+ let core = init_core_replay_preloaded(
21
29
  "timer_workflow_replay",
22
- &history_from_proto_binary("histories/timer_workflow_history.bin")
23
- .await
24
- .unwrap(),
30
+ [HistoryForReplay::new(
31
+ history_from_proto_binary("histories/timer_workflow_history.bin")
32
+ .await
33
+ .unwrap(),
34
+ "fake".to_owned(),
35
+ )],
25
36
  );
26
37
  let task = core.poll_workflow_activation().await.unwrap();
27
38
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
@@ -43,6 +54,14 @@ async fn timer_workflow_replay() {
43
54
  );
44
55
  };
45
56
  let poll_fut = async {
57
+ let evict_task = core
58
+ .poll_workflow_activation()
59
+ .await
60
+ .expect("Should be an eviction activation");
61
+ assert!(evict_task.eviction_reason().is_some());
62
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(evict_task.run_id))
63
+ .await
64
+ .unwrap();
46
65
  assert_matches!(
47
66
  core.poll_workflow_activation().await,
48
67
  Err(PollWfError::ShutDown)
@@ -64,11 +83,14 @@ async fn timer_workflow_replay() {
64
83
 
65
84
  #[tokio::test]
66
85
  async fn workflow_nondeterministic_replay() {
67
- let (core, _) = init_core_replay_preloaded(
86
+ let core = init_core_replay_preloaded(
68
87
  "timer_workflow_replay",
69
- &history_from_proto_binary("histories/timer_workflow_history.bin")
70
- .await
71
- .unwrap(),
88
+ [HistoryForReplay::new(
89
+ history_from_proto_binary("histories/timer_workflow_history.bin")
90
+ .await
91
+ .unwrap(),
92
+ "fake".to_owned(),
93
+ )],
72
94
  );
73
95
  let task = core.poll_workflow_activation().await.unwrap();
74
96
  core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
@@ -102,9 +124,7 @@ async fn replay_using_wf_function() {
102
124
  let num_timers = 10;
103
125
  let t = canned_histories::long_sequential_timers(num_timers as usize);
104
126
  let func = timers_wf(num_timers);
105
- let (worker, _) =
106
- init_core_replay_preloaded("replay_bench", &t.get_full_history_info().unwrap().into());
107
- let mut worker = Worker::new_from_core(worker, "replay_bench".to_string());
127
+ let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
108
128
  worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
109
129
  worker.run().await.unwrap();
110
130
  }
@@ -117,16 +137,69 @@ async fn replay_ok_ending_with_terminated_or_timed_out() {
117
137
  t2.add_workflow_execution_timed_out();
118
138
  for t in [t1, t2] {
119
139
  let func = timers_wf(1);
120
- let (worker, _) = init_core_replay_preloaded(
121
- "replay_ok_terminate",
122
- &t.get_full_history_info().unwrap().into(),
123
- );
124
- let mut worker = Worker::new_from_core(worker, "replay_ok_terminate".to_string());
140
+ let mut worker = replay_sdk_worker([test_hist_to_replay(t)]);
125
141
  worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
126
142
  worker.run().await.unwrap();
127
143
  }
128
144
  }
129
145
 
146
+ #[rstest::rstest]
147
+ #[tokio::test]
148
+ async fn multiple_histories_replay(#[values(false, true)] use_feeder: bool) {
149
+ let num_timers = 10;
150
+ let seq_timer_wf = timers_wf(num_timers);
151
+ let one_timer_wf = timers_wf(1);
152
+ let mut one_timer_hist = canned_histories::single_timer("1");
153
+ one_timer_hist.set_wf_type("onetimer");
154
+ let mut seq_timer_hist = canned_histories::long_sequential_timers(num_timers as usize);
155
+ seq_timer_hist.set_wf_type("seqtimer");
156
+ let (feeder, stream) = HistoryFeeder::new(1);
157
+ let mut worker = if use_feeder {
158
+ replay_sdk_worker_stream(stream)
159
+ } else {
160
+ replay_sdk_worker([
161
+ test_hist_to_replay(one_timer_hist.clone()),
162
+ test_hist_to_replay(seq_timer_hist.clone()),
163
+ ])
164
+ };
165
+ let runs_ctr_i = UniqueRunsCounter::default();
166
+ let runs_ctr = runs_ctr_i.runs.clone();
167
+ worker.set_worker_interceptor(Box::new(runs_ctr_i));
168
+ worker.register_wf("onetimer", one_timer_wf);
169
+ worker.register_wf("seqtimer", seq_timer_wf);
170
+
171
+ if use_feeder {
172
+ let feed_fut = async move {
173
+ feeder
174
+ .feed(test_hist_to_replay(one_timer_hist))
175
+ .await
176
+ .unwrap();
177
+ feeder
178
+ .feed(test_hist_to_replay(seq_timer_hist))
179
+ .await
180
+ .unwrap();
181
+ };
182
+ let (_, runr) = join!(feed_fut, worker.run());
183
+ runr.unwrap();
184
+ } else {
185
+ worker.run().await.unwrap();
186
+ }
187
+ assert_eq!(runs_ctr.lock().len(), 2);
188
+ }
189
+
190
+ #[tokio::test]
191
+ async fn multiple_histories_can_handle_dupe_run_ids() {
192
+ let mut hist1 = canned_histories::single_timer("1");
193
+ hist1.set_wf_type("onetimer");
194
+ let mut worker = replay_sdk_worker([
195
+ test_hist_to_replay(hist1.clone()),
196
+ test_hist_to_replay(hist1.clone()),
197
+ test_hist_to_replay(hist1),
198
+ ]);
199
+ worker.register_wf("onetimer", timers_wf(1));
200
+ worker.run().await.unwrap();
201
+ }
202
+
130
203
  fn timers_wf(num_timers: u32) -> WorkflowFunction {
131
204
  WorkflowFunction::new(move |ctx: WfContext| async move {
132
205
  for _ in 1..=num_timers {
@@ -135,3 +208,16 @@ fn timers_wf(num_timers: u32) -> WorkflowFunction {
135
208
  Ok(().into())
136
209
  })
137
210
  }
211
+
212
+ #[derive(Default)]
213
+ struct UniqueRunsCounter {
214
+ runs: Arc<Mutex<HashSet<String>>>,
215
+ }
216
+ #[async_trait::async_trait(?Send)]
217
+ impl WorkerInterceptor for UniqueRunsCounter {
218
+ async fn on_workflow_activation_completion(&self, completion: &WorkflowActivationCompletion) {
219
+ self.runs.lock().insert(completion.run_id.clone());
220
+ }
221
+
222
+ fn on_shutdown(&self, _: &Worker) {}
223
+ }
@@ -6,6 +6,7 @@ mod child_workflows;
6
6
  mod continue_as_new;
7
7
  mod determinism;
8
8
  mod local_activities;
9
+ mod modify_wf_properties;
9
10
  mod patches;
10
11
  mod replay;
11
12
  mod resets;
@@ -28,6 +29,7 @@ use temporal_client::{WorkflowClientTrait, WorkflowOptions};
28
29
  use temporal_sdk::{
29
30
  interceptors::WorkerInterceptor, ActContext, ActivityOptions, WfContext, WorkflowResult,
30
31
  };
32
+ use temporal_sdk_core::replay::HistoryForReplay;
31
33
  use temporal_sdk_core_api::{errors::PollWfError, Worker};
32
34
  use temporal_sdk_core_protos::{
33
35
  coresdk::{
@@ -37,7 +39,7 @@ use temporal_sdk_core_protos::{
37
39
  workflow_completion::WorkflowActivationCompletion,
38
40
  ActivityTaskCompletion, AsJsonPayloadExt, IntoCompletion,
39
41
  },
40
- temporal::api::failure::v1::Failure,
42
+ temporal::api::{failure::v1::Failure, history::v1::history_event},
41
43
  };
42
44
  use temporal_sdk_core_test_utils::{
43
45
  history_from_proto_binary, init_core_and_create_wf, init_core_replay_preloaded,
@@ -102,6 +104,7 @@ async fn parallel_workflows_same_queue() {
102
104
  for handle in handles {
103
105
  handle.await.unwrap()
104
106
  }
107
+ core.shutdown().await;
105
108
  }
106
109
 
107
110
  static RUN_CT: AtomicUsize = AtomicUsize::new(0);
@@ -179,13 +182,20 @@ async fn shutdown_aborts_actively_blocked_poll() {
179
182
  #[tokio::test]
180
183
  async fn fail_wf_task(#[values(true, false)] replay: bool) {
181
184
  let core = if replay {
182
- let (core, _) = init_core_replay_preloaded(
183
- "fail_wf_task",
184
- &history_from_proto_binary("histories/fail_wf_task.bin")
185
- .await
186
- .unwrap(),
187
- );
188
- core
185
+ // We need to send the history twice, since we fail it the first time.
186
+ let mut hist_proto = history_from_proto_binary("histories/fail_wf_task.bin")
187
+ .await
188
+ .unwrap();
189
+ let hist = HistoryForReplay::new(hist_proto.clone(), "fake".to_string());
190
+ if let Some(history_event::Attributes::WorkflowExecutionStartedEventAttributes(
191
+ ref mut attrs,
192
+ )) = hist_proto.events[0].attributes
193
+ {
194
+ attrs.original_execution_run_id = "run2".to_string();
195
+ attrs.first_execution_run_id = "run2".to_string();
196
+ }
197
+ let hist2 = HistoryForReplay::new(hist_proto, "fake".to_string());
198
+ init_core_replay_preloaded("fail_wf_task", [hist, hist2])
189
199
  } else {
190
200
  let mut starter = init_core_and_create_wf("fail_wf_task").await;
191
201
  starter.get_worker().await
@@ -20,6 +20,7 @@ mod integ_tests {
20
20
  mod client_tests;
21
21
  mod ephemeral_server_tests;
22
22
  mod heartbeat_tests;
23
+ mod metrics_tests;
23
24
  mod polling_tests;
24
25
  mod queries_tests;
25
26
  mod visibility_tests;
@@ -28,10 +29,10 @@ mod integ_tests {
28
29
  use std::str::FromStr;
29
30
  use temporal_client::WorkflowService;
30
31
  use temporal_sdk_core::{
31
- init_worker, telemetry_init, ClientOptionsBuilder, ClientTlsConfig, TlsConfig,
32
+ init_worker, ClientOptionsBuilder, ClientTlsConfig, CoreRuntime, TlsConfig,
32
33
  WorkflowClientTrait,
33
34
  };
34
- use temporal_sdk_core_api::{worker::WorkerConfigBuilder, CoreTelemetry};
35
+ use temporal_sdk_core_api::worker::WorkerConfigBuilder;
35
36
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::ListNamespacesRequest;
36
37
  use temporal_sdk_core_test_utils::{
37
38
  get_integ_server_options, get_integ_telem_options, NAMESPACE,
@@ -43,13 +44,14 @@ mod integ_tests {
43
44
  #[ignore] // Really a compile time check more than anything
44
45
  async fn lang_bridge_example() {
45
46
  let opts = get_integ_server_options();
46
- let telem_d = telemetry_init(&get_integ_telem_options()).unwrap();
47
+ let runtime = CoreRuntime::new_assume_tokio(get_integ_telem_options()).unwrap();
47
48
  let mut retrying_client = opts
48
- .connect_no_namespace(telem_d.get_metric_meter(), None)
49
+ .connect_no_namespace(runtime.metric_meter(), None)
49
50
  .await
50
51
  .unwrap();
51
52
 
52
53
  let _worker = init_worker(
54
+ &runtime,
53
55
  WorkerConfigBuilder::default()
54
56
  .namespace("default")
55
57
  .task_queue("Wheee!")