temporalio 0.0.1 → 0.0.2

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 (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!")