temporalio 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (310) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +180 -7
  3. data/bridge/Cargo.lock +208 -76
  4. data/bridge/Cargo.toml +5 -2
  5. data/bridge/sdk-core/Cargo.toml +1 -1
  6. data/bridge/sdk-core/README.md +20 -10
  7. data/bridge/sdk-core/client/Cargo.toml +1 -1
  8. data/bridge/sdk-core/client/src/lib.rs +227 -59
  9. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  10. data/bridge/sdk-core/client/src/raw.rs +13 -12
  11. data/bridge/sdk-core/client/src/retry.rs +132 -43
  12. data/bridge/sdk-core/core/Cargo.toml +28 -15
  13. data/bridge/sdk-core/core/benches/workflow_replay.rs +13 -10
  14. data/bridge/sdk-core/core/src/abstractions.rs +225 -36
  15. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +217 -79
  16. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  17. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +565 -34
  18. data/bridge/sdk-core/core/src/core_tests/queries.rs +247 -90
  19. data/bridge/sdk-core/core/src/core_tests/workers.rs +3 -5
  20. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +1 -1
  21. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +430 -67
  22. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  23. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  24. data/bridge/sdk-core/core/src/lib.rs +148 -34
  25. data/bridge/sdk-core/core/src/protosext/mod.rs +1 -1
  26. data/bridge/sdk-core/core/src/replay/mod.rs +185 -41
  27. data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
  28. data/bridge/sdk-core/core/src/telemetry/metrics.rs +219 -140
  29. data/bridge/sdk-core/core/src/telemetry/mod.rs +326 -315
  30. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +20 -14
  31. data/bridge/sdk-core/core/src/test_help/mod.rs +85 -21
  32. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  33. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  34. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +364 -128
  35. data/bridge/sdk-core/core/src/worker/activities.rs +263 -170
  36. data/bridge/sdk-core/core/src/worker/client/mocks.rs +23 -3
  37. data/bridge/sdk-core/core/src/worker/client.rs +48 -6
  38. data/bridge/sdk-core/core/src/worker/mod.rs +186 -75
  39. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  40. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +13 -24
  41. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +879 -226
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +101 -48
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +8 -12
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +6 -9
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +90 -32
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +6 -9
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -10
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +6 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +160 -83
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +36 -54
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +179 -0
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +104 -157
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +8 -12
  54. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -13
  55. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +10 -4
  56. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -11
  57. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  58. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +395 -299
  59. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +12 -20
  60. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +33 -18
  61. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1032 -374
  62. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +525 -392
  63. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  64. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +3 -6
  66. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +456 -681
  69. data/bridge/sdk-core/core-api/Cargo.toml +6 -4
  70. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  71. data/bridge/sdk-core/core-api/src/lib.rs +7 -45
  72. data/bridge/sdk-core/core-api/src/telemetry.rs +141 -0
  73. data/bridge/sdk-core/core-api/src/worker.rs +27 -1
  74. data/bridge/sdk-core/etc/deps.svg +115 -140
  75. data/bridge/sdk-core/etc/regen-depgraph.sh +5 -0
  76. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  77. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +1 -1
  78. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  79. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  80. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  81. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  82. data/bridge/sdk-core/protos/api_upstream/buf.yaml +0 -3
  83. data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
  84. data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
  85. data/bridge/sdk-core/protos/api_upstream/{temporal/api/enums/v1/cluster.proto → build/tools.go} +7 -18
  86. data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
  87. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +12 -9
  88. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +15 -26
  89. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  90. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  91. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +4 -9
  92. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  93. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +10 -8
  94. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +28 -2
  95. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  96. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  97. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  98. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  99. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  100. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  101. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  102. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  103. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  104. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  105. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +62 -26
  106. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  107. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +24 -61
  108. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -21
  109. data/bridge/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  110. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  111. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  112. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +110 -31
  113. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  114. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +4 -4
  115. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  116. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  117. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +3 -2
  118. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +111 -36
  119. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +19 -5
  120. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  121. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  122. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  123. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  124. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  125. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  126. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +9 -0
  127. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +9 -1
  128. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  129. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  130. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  131. data/bridge/sdk-core/sdk/Cargo.toml +4 -3
  132. data/bridge/sdk-core/sdk/src/interceptors.rs +36 -3
  133. data/bridge/sdk-core/sdk/src/lib.rs +94 -25
  134. data/bridge/sdk-core/sdk/src/workflow_context.rs +13 -2
  135. data/bridge/sdk-core/sdk/src/workflow_future.rs +10 -13
  136. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  137. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -2
  138. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +164 -104
  139. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +27 -23
  140. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +252 -74
  141. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  142. data/bridge/sdk-core/test-utils/Cargo.toml +4 -1
  143. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  144. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  145. data/bridge/sdk-core/test-utils/src/lib.rs +161 -50
  146. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  147. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  148. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  149. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  150. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  151. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  152. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +239 -0
  153. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -60
  154. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  155. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  156. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  157. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  158. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  159. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  160. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  161. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  162. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +151 -116
  163. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +54 -0
  164. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  165. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +115 -24
  166. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  167. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  168. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  169. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  170. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  171. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +27 -18
  172. data/bridge/sdk-core/tests/main.rs +8 -16
  173. data/bridge/sdk-core/tests/runner.rs +75 -36
  174. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  175. data/bridge/src/connection.rs +117 -82
  176. data/bridge/src/lib.rs +356 -42
  177. data/bridge/src/runtime.rs +10 -3
  178. data/bridge/src/test_server.rs +153 -0
  179. data/bridge/src/worker.rs +133 -9
  180. data/lib/gen/temporal/api/batch/v1/message_pb.rb +8 -6
  181. data/lib/gen/temporal/api/command/v1/message_pb.rb +10 -16
  182. data/lib/gen/temporal/api/common/v1/message_pb.rb +5 -1
  183. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +2 -1
  184. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +3 -3
  185. data/lib/gen/temporal/api/enums/v1/common_pb.rb +2 -1
  186. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +5 -4
  187. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +9 -1
  188. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +1 -1
  189. data/lib/gen/temporal/api/enums/v1/query_pb.rb +1 -1
  190. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +1 -1
  191. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +1 -1
  192. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +1 -1
  193. data/lib/gen/temporal/api/enums/v1/update_pb.rb +7 -10
  194. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +1 -1
  195. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +1 -1
  196. data/lib/gen/temporal/api/failure/v1/message_pb.rb +1 -1
  197. data/lib/gen/temporal/api/filter/v1/message_pb.rb +1 -1
  198. data/lib/gen/temporal/api/history/v1/message_pb.rb +34 -25
  199. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +2 -1
  200. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +14 -51
  201. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +1 -1
  202. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  203. data/lib/gen/temporal/api/query/v1/message_pb.rb +1 -1
  204. data/lib/gen/temporal/api/replication/v1/message_pb.rb +1 -1
  205. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +22 -1
  206. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  207. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +2 -2
  208. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  209. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  210. data/lib/gen/temporal/api/update/v1/message_pb.rb +49 -3
  211. data/lib/gen/temporal/api/version/v1/message_pb.rb +1 -1
  212. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +2 -1
  213. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +47 -20
  214. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +1 -1
  215. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  216. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  217. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  218. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  219. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  220. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  221. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +28 -21
  222. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +32 -24
  223. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  224. data/lib/temporalio/activity/context.rb +102 -0
  225. data/lib/temporalio/activity/info.rb +67 -0
  226. data/lib/temporalio/activity.rb +85 -0
  227. data/lib/temporalio/bridge/connect_options.rb +15 -0
  228. data/lib/temporalio/bridge/error.rb +8 -0
  229. data/lib/temporalio/bridge/retry_config.rb +24 -0
  230. data/lib/temporalio/bridge/tls_options.rb +19 -0
  231. data/lib/temporalio/bridge.rb +14 -0
  232. data/lib/{temporal → temporalio}/client/implementation.rb +57 -56
  233. data/lib/{temporal → temporalio}/client/workflow_handle.rb +35 -35
  234. data/lib/{temporal → temporalio}/client.rb +19 -32
  235. data/lib/temporalio/connection/retry_config.rb +44 -0
  236. data/lib/temporalio/connection/service.rb +20 -0
  237. data/lib/temporalio/connection/test_service.rb +92 -0
  238. data/lib/temporalio/connection/tls_options.rb +51 -0
  239. data/lib/temporalio/connection/workflow_service.rb +731 -0
  240. data/lib/temporalio/connection.rb +86 -0
  241. data/lib/{temporal → temporalio}/data_converter.rb +76 -35
  242. data/lib/{temporal → temporalio}/error/failure.rb +6 -6
  243. data/lib/{temporal → temporalio}/error/workflow_failure.rb +4 -2
  244. data/lib/{temporal → temporalio}/errors.rb +19 -1
  245. data/lib/{temporal → temporalio}/failure_converter/base.rb +5 -5
  246. data/lib/{temporal → temporalio}/failure_converter/basic.rb +58 -52
  247. data/lib/temporalio/failure_converter.rb +7 -0
  248. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  249. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  250. data/lib/{temporal → temporalio}/interceptor/chain.rb +7 -6
  251. data/lib/{temporal → temporalio}/interceptor/client.rb +27 -2
  252. data/lib/temporalio/interceptor.rb +22 -0
  253. data/lib/{temporal → temporalio}/payload_codec/base.rb +5 -5
  254. data/lib/{temporal → temporalio}/payload_converter/base.rb +3 -3
  255. data/lib/{temporal → temporalio}/payload_converter/bytes.rb +4 -3
  256. data/lib/{temporal → temporalio}/payload_converter/composite.rb +7 -5
  257. data/lib/{temporal → temporalio}/payload_converter/encoding_base.rb +4 -4
  258. data/lib/{temporal → temporalio}/payload_converter/json.rb +4 -3
  259. data/lib/{temporal → temporalio}/payload_converter/nil.rb +4 -3
  260. data/lib/temporalio/payload_converter.rb +14 -0
  261. data/lib/{temporal → temporalio}/retry_policy.rb +17 -7
  262. data/lib/{temporal → temporalio}/retry_state.rb +1 -1
  263. data/lib/temporalio/runtime.rb +25 -0
  264. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  265. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  266. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  267. data/lib/temporalio/testing.rb +175 -0
  268. data/lib/{temporal → temporalio}/timeout_type.rb +2 -2
  269. data/lib/temporalio/version.rb +3 -0
  270. data/lib/temporalio/worker/activity_runner.rb +114 -0
  271. data/lib/temporalio/worker/activity_worker.rb +164 -0
  272. data/lib/temporalio/worker/reactor.rb +46 -0
  273. data/lib/temporalio/worker/runner.rb +63 -0
  274. data/lib/temporalio/worker/sync_worker.rb +124 -0
  275. data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
  276. data/lib/temporalio/worker.rb +204 -0
  277. data/lib/temporalio/workflow/async.rb +46 -0
  278. data/lib/{temporal → temporalio}/workflow/execution_info.rb +4 -4
  279. data/lib/{temporal → temporalio}/workflow/execution_status.rb +1 -1
  280. data/lib/temporalio/workflow/future.rb +138 -0
  281. data/lib/{temporal → temporalio}/workflow/id_reuse_policy.rb +6 -6
  282. data/lib/temporalio/workflow/info.rb +76 -0
  283. data/lib/{temporal → temporalio}/workflow/query_reject_condition.rb +5 -5
  284. data/lib/temporalio.rb +12 -3
  285. data/temporalio.gemspec +11 -6
  286. metadata +137 -64
  287. data/bridge/sdk-core/Cargo.lock +0 -2606
  288. data/bridge/sdk-core/bridge-ffi/Cargo.toml +0 -24
  289. data/bridge/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  290. data/bridge/sdk-core/bridge-ffi/build.rs +0 -25
  291. data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -249
  292. data/bridge/sdk-core/bridge-ffi/src/lib.rs +0 -825
  293. data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +0 -211
  294. data/bridge/sdk-core/core/src/log_export.rs +0 -62
  295. data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +0 -127
  296. data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +0 -71
  297. data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +0 -83
  298. data/bridge/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  299. data/bridge/sdk-core/sdk/src/conversions.rs +0 -8
  300. data/lib/bridge.so +0 -0
  301. data/lib/gen/temporal/api/cluster/v1/message_pb.rb +0 -67
  302. data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +0 -26
  303. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
  304. data/lib/temporal/bridge.rb +0 -14
  305. data/lib/temporal/connection.rb +0 -736
  306. data/lib/temporal/failure_converter.rb +0 -8
  307. data/lib/temporal/payload_converter.rb +0 -14
  308. data/lib/temporal/runtime.rb +0 -22
  309. data/lib/temporal/version.rb +0 -3
  310. data/lib/temporal.rb +0 -8
@@ -23,6 +23,6 @@ async fn main() -> Result<(), anyhow::Error> {
23
23
  .expect("history field must be populated");
24
24
  // Serialize history to file
25
25
  let byteified = hist.encode_to_vec();
26
- tokio::fs::write(format!("{}_history.bin", wf_id), &byteified).await?;
26
+ tokio::fs::write(format!("{wf_id}_history.bin"), &byteified).await?;
27
27
  Ok(())
28
28
  }
@@ -5,8 +5,14 @@
5
5
  extern crate tracing;
6
6
 
7
7
  pub mod canned_histories;
8
+ pub mod wf_input_saver;
9
+ pub mod workflows;
8
10
 
9
- use crate::stream::TryStreamExt;
11
+ use crate::{
12
+ stream::{Stream, TryStreamExt},
13
+ wf_input_saver::stream_to_file,
14
+ };
15
+ use base64::{prelude::BASE64_STANDARD, Engine};
10
16
  use futures::{future, stream, stream::FuturesUnordered, StreamExt};
11
17
  use parking_lot::Mutex;
12
18
  use prost::Message;
@@ -18,14 +24,24 @@ use std::{
18
24
  use temporal_client::{
19
25
  Client, RetryClient, WorkflowClientTrait, WorkflowExecutionInfo, WorkflowOptions,
20
26
  };
21
- use temporal_sdk::{interceptors::WorkerInterceptor, IntoActivityFunc, Worker, WorkflowFunction};
27
+ use temporal_sdk::{
28
+ interceptors::{FailOnNondeterminismInterceptor, WorkerInterceptor},
29
+ IntoActivityFunc, Worker, WorkflowFunction,
30
+ };
22
31
  use temporal_sdk_core::{
23
32
  ephemeral_server::{EphemeralExe, EphemeralExeVersion},
24
- init_replay_worker, init_worker, telemetry_init, ClientOptions, ClientOptionsBuilder, Logger,
25
- MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
26
- TraceExporter, WorkerConfig, WorkerConfigBuilder,
33
+ init_replay_worker, init_worker,
34
+ replay::HistoryForReplay,
35
+ ClientOptions, ClientOptionsBuilder, CoreRuntime, WorkerConfig, WorkerConfigBuilder,
36
+ };
37
+ use temporal_sdk_core_api::errors::{PollActivityError, PollWfError};
38
+ use temporal_sdk_core_api::{
39
+ telemetry::{
40
+ Logger, MetricsExporter, OtelCollectorOptions, TelemetryOptions, TelemetryOptionsBuilder,
41
+ TraceExportConfig, TraceExporter,
42
+ },
43
+ Worker as CoreWorker,
27
44
  };
28
- use temporal_sdk_core_api::Worker as CoreWorker;
29
45
  use temporal_sdk_core_protos::{
30
46
  coresdk::{
31
47
  workflow_commands::{
@@ -35,16 +51,17 @@ use temporal_sdk_core_protos::{
35
51
  workflow_completion::WorkflowActivationCompletion,
36
52
  },
37
53
  temporal::api::{common::v1::Payload, history::v1::History},
54
+ DEFAULT_ACTIVITY_TYPE,
38
55
  };
39
- use tokio::sync::OnceCell;
56
+ use tokio::sync::{mpsc::unbounded_channel, OnceCell};
40
57
  use url::Url;
41
58
 
42
59
  pub const NAMESPACE: &str = "default";
43
60
  pub const TEST_Q: &str = "q";
44
61
  /// The env var used to specify where the integ tests should point
45
62
  pub const INTEG_SERVER_TARGET_ENV_VAR: &str = "TEMPORAL_SERVICE_ADDRESS";
46
- /// This env var is set (to any value) if temporalite is in use
47
- pub const INTEG_TEMPORALITE_USED_ENV_VAR: &str = "INTEG_TEMPORALITE_ON";
63
+ /// This env var is set (to any value) if temporal CLI dev server is in use
64
+ pub const INTEG_TEMPORAL_DEV_SERVER_USED_ENV_VAR: &str = "INTEG_TEMPORAL_DEV_SERVER_ON";
48
65
  /// This env var is set (to any value) if the test server is in use
49
66
  pub const INTEG_TEST_SERVER_USED_ENV_VAR: &str = "INTEG_TEST_SERVER_ON";
50
67
 
@@ -52,6 +69,15 @@ pub const INTEG_TEST_SERVER_USED_ENV_VAR: &str = "INTEG_TEST_SERVER_ON";
52
69
  const OTEL_URL_ENV_VAR: &str = "TEMPORAL_INTEG_OTEL_URL";
53
70
  /// If set, enable direct scraping of prom metrics on the specified port
54
71
  const PROM_ENABLE_ENV_VAR: &str = "TEMPORAL_INTEG_PROM_PORT";
72
+ #[macro_export]
73
+ macro_rules! prost_dur {
74
+ ($dur_call:ident $args:tt) => {
75
+ std::time::Duration::$dur_call$args
76
+ .try_into()
77
+ .expect("test duration fits")
78
+ };
79
+ }
80
+
55
81
  /// Create a worker instance which will use the provided test name to base the task queue and wf id
56
82
  /// upon. Returns the instance and the task queue name (which is also the workflow id).
57
83
  pub async fn init_core_and_create_wf(test_name: &str) -> CoreWfStarter {
@@ -61,21 +87,44 @@ pub async fn init_core_and_create_wf(test_name: &str) -> CoreWfStarter {
61
87
  starter
62
88
  }
63
89
 
64
- /// Create a worker replay instance preloaded with a provided history. Returns the worker impl
65
- /// and the task queue name as in [init_core_and_create_wf].
66
- pub fn init_core_replay_preloaded(
67
- test_name: &str,
68
- history: &History,
69
- ) -> (Arc<dyn CoreWorker>, String) {
70
- telemetry_init(&get_integ_telem_options()).expect("Telemetry inits cleanly");
90
+ /// Create a worker replay instance preloaded with provided histories. Returns the worker impl.
91
+ pub fn init_core_replay_preloaded<I>(test_name: &str, histories: I) -> Arc<dyn CoreWorker>
92
+ where
93
+ I: IntoIterator<Item = HistoryForReplay> + 'static,
94
+ <I as IntoIterator>::IntoIter: Send,
95
+ {
96
+ init_core_replay_stream(test_name, stream::iter(histories))
97
+ }
98
+ pub fn init_core_replay_stream<I>(test_name: &str, histories: I) -> Arc<dyn CoreWorker>
99
+ where
100
+ I: Stream<Item = HistoryForReplay> + Send + 'static,
101
+ {
102
+ init_integ_telem();
71
103
  let worker_cfg = WorkerConfigBuilder::default()
72
104
  .namespace(NAMESPACE)
73
105
  .task_queue(test_name)
74
106
  .worker_build_id("test_bin_id")
75
107
  .build()
76
108
  .expect("Configuration options construct properly");
77
- let worker = init_replay_worker(worker_cfg, history).expect("Replay worker must init properly");
78
- (Arc::new(worker), test_name.to_string())
109
+ let worker =
110
+ init_replay_worker(worker_cfg, histories).expect("Replay worker must init properly");
111
+ Arc::new(worker)
112
+ }
113
+ pub fn replay_sdk_worker<I>(histories: I) -> Worker
114
+ where
115
+ I: IntoIterator<Item = HistoryForReplay> + 'static,
116
+ <I as IntoIterator>::IntoIter: Send,
117
+ {
118
+ replay_sdk_worker_stream(stream::iter(histories))
119
+ }
120
+ pub fn replay_sdk_worker_stream<I>(histories: I) -> Worker
121
+ where
122
+ I: Stream<Item = HistoryForReplay> + Send + 'static,
123
+ {
124
+ let core = init_core_replay_stream("replay_worker_test", histories);
125
+ let mut worker = Worker::new_from_core(core, "replay_q".to_string());
126
+ worker.set_worker_interceptor(Box::new(FailOnNondeterminismInterceptor {}));
127
+ worker
79
128
  }
80
129
 
81
130
  /// Load history from a file containing the protobuf serialization of it
@@ -87,13 +136,24 @@ pub async fn history_from_proto_binary(path_from_root: &str) -> Result<History,
87
136
  Ok(History::decode(&*bytes)?)
88
137
  }
89
138
 
139
+ static INTEG_TESTS_RT: once_cell::sync::OnceCell<CoreRuntime> = once_cell::sync::OnceCell::new();
140
+ pub fn init_integ_telem() {
141
+ INTEG_TESTS_RT.get_or_init(|| {
142
+ let telemetry_options = get_integ_telem_options();
143
+ let rt =
144
+ CoreRuntime::new_assume_tokio(telemetry_options).expect("Core runtime inits cleanly");
145
+ let _ = tracing::subscriber::set_global_default(rt.trace_subscriber());
146
+ rt
147
+ });
148
+ }
149
+
90
150
  /// Implements a builder pattern to help integ tests initialize core and create workflows
91
151
  pub struct CoreWfStarter {
92
152
  /// Used for both the task queue and workflow id
93
153
  task_queue_name: String,
94
- telemetry_options: TelemetryOptions,
95
154
  pub worker_config: WorkerConfig,
96
- wft_timeout: Option<Duration>,
155
+ /// Options to use when starting workflow(s)
156
+ pub workflow_options: WorkflowOptions,
97
157
  initted_worker: OnceCell<InitializedWorker>,
98
158
  }
99
159
  struct InitializedWorker {
@@ -103,16 +163,12 @@ struct InitializedWorker {
103
163
 
104
164
  impl CoreWfStarter {
105
165
  pub fn new(test_name: &str) -> Self {
166
+ init_integ_telem();
106
167
  let rand_bytes: Vec<u8> = rand::thread_rng().sample_iter(&Standard).take(6).collect();
107
- let task_q_salt = base64::encode(rand_bytes);
108
- let task_queue = format!("{}_{}", test_name, task_q_salt);
109
- Self::new_tq_name(&task_queue)
110
- }
111
-
112
- pub fn new_tq_name(task_queue: &str) -> Self {
168
+ let task_q_salt = BASE64_STANDARD.encode(rand_bytes);
169
+ let task_queue = format!("{test_name}_{task_q_salt}");
113
170
  Self {
114
171
  task_queue_name: task_queue.to_owned(),
115
- telemetry_options: get_integ_telem_options(),
116
172
  worker_config: WorkerConfigBuilder::default()
117
173
  .namespace(NAMESPACE)
118
174
  .task_queue(task_queue)
@@ -120,8 +176,8 @@ impl CoreWfStarter {
120
176
  .max_cached_workflows(1000_usize)
121
177
  .build()
122
178
  .unwrap(),
123
- wft_timeout: None,
124
179
  initted_worker: OnceCell::new(),
180
+ workflow_options: Default::default(),
125
181
  }
126
182
  }
127
183
 
@@ -148,13 +204,27 @@ impl CoreWfStarter {
148
204
  }
149
205
 
150
206
  /// Start the workflow defined by the builder and return run id
151
- pub async fn start_wf(&self) -> String {
152
- self.start_wf_with_id(self.task_queue_name.clone(), WorkflowOptions::default())
207
+ pub async fn start_wf(&mut self) -> String {
208
+ self.start_wf_with_id(self.task_queue_name.clone()).await
209
+ }
210
+
211
+ pub async fn start_with_worker(
212
+ &self,
213
+ wf_name: impl Into<String>,
214
+ worker: &mut TestWorker,
215
+ ) -> String {
216
+ worker
217
+ .submit_wf(
218
+ self.task_queue_name.clone(),
219
+ wf_name.into(),
220
+ vec![],
221
+ self.workflow_options.clone(),
222
+ )
153
223
  .await
224
+ .unwrap()
154
225
  }
155
226
 
156
- pub async fn start_wf_with_id(&self, workflow_id: String, mut opts: WorkflowOptions) -> String {
157
- opts.task_timeout = opts.task_timeout.or(self.wft_timeout);
227
+ pub async fn start_wf_with_id(&self, workflow_id: String) -> String {
158
228
  self.initted_worker
159
229
  .get()
160
230
  .expect(
@@ -168,7 +238,7 @@ impl CoreWfStarter {
168
238
  workflow_id,
169
239
  self.task_queue_name.clone(),
170
240
  None,
171
- opts,
241
+ self.workflow_options.clone(),
172
242
  )
173
243
  .await
174
244
  .unwrap()
@@ -181,19 +251,21 @@ impl CoreWfStarter {
181
251
  &mut self,
182
252
  wf_id: impl Into<String>,
183
253
  run_id: impl Into<String>,
184
- // TODO: Need not be passed in
185
254
  worker: &mut Worker,
186
255
  ) -> Result<(), anyhow::Error> {
256
+ let wf_id = wf_id.into();
187
257
  // Fetch history and replay it
188
258
  let history = self
189
259
  .get_client()
190
260
  .await
191
- .get_workflow_execution_history(wf_id.into(), Some(run_id.into()), vec![])
261
+ .get_workflow_execution_history(wf_id.clone(), Some(run_id.into()), vec![])
192
262
  .await?
193
263
  .history
194
264
  .expect("history field must be populated");
195
- let (replay_worker, _) = init_core_replay_preloaded(worker.task_queue(), &history);
265
+ let with_id = HistoryForReplay::new(history, wf_id);
266
+ let replay_worker = init_core_replay_preloaded(worker.task_queue(), [with_id]);
196
267
  worker.with_new_core_worker(replay_worker);
268
+ worker.set_worker_interceptor(Box::new(FailOnNondeterminismInterceptor {}));
197
269
  worker.run().await.unwrap();
198
270
  Ok(())
199
271
  }
@@ -231,22 +303,36 @@ impl CoreWfStarter {
231
303
  self
232
304
  }
233
305
 
234
- pub fn wft_timeout(&mut self, timeout: Duration) -> &mut Self {
235
- self.wft_timeout = Some(timeout);
306
+ pub fn no_remote_activities(&mut self) -> &mut Self {
307
+ self.worker_config.no_remote_activities = true;
308
+ self
309
+ }
310
+
311
+ pub fn enable_wf_state_input_recording(&mut self) -> &mut Self {
312
+ let (ser_tx, ser_rx) = unbounded_channel();
313
+ let worker_cfg_clone = self.worker_config.clone();
314
+ tokio::spawn(async move {
315
+ stream_to_file(&worker_cfg_clone, ser_rx).await.unwrap();
316
+ });
317
+ self.worker_config.wf_state_inputs = Some(ser_tx);
236
318
  self
237
319
  }
238
320
 
239
321
  async fn get_or_init(&mut self) -> &InitializedWorker {
240
322
  self.initted_worker
241
323
  .get_or_init(|| async {
242
- telemetry_init(&self.telemetry_options).expect("Telemetry inits cleanly");
243
324
  let client = Arc::new(
244
325
  get_integ_server_options()
245
326
  .connect(self.worker_config.namespace.clone(), None, None)
246
327
  .await
247
328
  .expect("Must connect"),
248
329
  );
249
- let worker = init_worker(self.worker_config.clone(), client.clone());
330
+ let worker = init_worker(
331
+ INTEG_TESTS_RT.get().unwrap(),
332
+ self.worker_config.clone(),
333
+ client.clone(),
334
+ )
335
+ .expect("Worker inits cleanly");
250
336
  InitializedWorker {
251
337
  worker: Arc::new(worker),
252
338
  client,
@@ -442,7 +528,6 @@ impl WorkerInterceptor for TestWorkerCompletionIceptor {
442
528
 
443
529
  /// Returns the client options used to connect to the server used for integration tests.
444
530
  pub fn get_integ_server_options() -> ClientOptions {
445
- telemetry_init(&get_integ_telem_options()).expect("Telemetry inits cleanly");
446
531
  let temporal_server_address = match env::var(INTEG_SERVER_TARGET_ENV_VAR) {
447
532
  Ok(addr) => addr,
448
533
  Err(_) => "http://localhost:7233".to_owned(),
@@ -459,6 +544,8 @@ pub fn get_integ_server_options() -> ClientOptions {
459
544
 
460
545
  pub fn get_integ_telem_options() -> TelemetryOptions {
461
546
  let mut ob = TelemetryOptionsBuilder::default();
547
+ let filter_string =
548
+ env::var("RUST_LOG").unwrap_or_else(|_| "temporal_sdk_core=INFO".to_string());
462
549
  if let Some(url) = env::var(OTEL_URL_ENV_VAR)
463
550
  .ok()
464
551
  .map(|x| x.parse::<Url>().unwrap())
@@ -466,8 +553,12 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
466
553
  let opts = OtelCollectorOptions {
467
554
  url,
468
555
  headers: Default::default(),
556
+ metric_periodicity: None,
469
557
  };
470
- ob.tracing(TraceExporter::Otel(opts.clone()));
558
+ ob.tracing(TraceExportConfig {
559
+ filter: filter_string.clone(),
560
+ exporter: TraceExporter::Otel(opts.clone()),
561
+ });
471
562
  ob.metrics(MetricsExporter::Otel(opts));
472
563
  }
473
564
  if let Some(addr) = env::var(PROM_ENABLE_ENV_VAR)
@@ -476,15 +567,16 @@ pub fn get_integ_telem_options() -> TelemetryOptions {
476
567
  {
477
568
  ob.metrics(MetricsExporter::Prometheus(addr));
478
569
  }
479
- ob.tracing_filter(env::var("RUST_LOG").unwrap_or_else(|_| "temporal_sdk_core=INFO".to_string()))
480
- .logging(Logger::Console)
481
- .build()
482
- .unwrap()
570
+ ob.logging(Logger::Console {
571
+ filter: filter_string,
572
+ })
573
+ .build()
574
+ .unwrap()
483
575
  }
484
576
 
485
577
  pub fn default_cached_download() -> EphemeralExe {
486
578
  EphemeralExe::CachedDownload {
487
- version: EphemeralExeVersion::Default {
579
+ version: EphemeralExeVersion::SDKDefault {
488
580
  sdk_name: "sdk-rust".to_string(),
489
581
  sdk_version: "0.1.0".to_string(),
490
582
  },
@@ -503,8 +595,7 @@ pub fn schedule_activity_cmd(
503
595
  ScheduleActivity {
504
596
  seq,
505
597
  activity_id: activity_id.to_string(),
506
- activity_type: "test_activity".to_string(),
507
- namespace: NAMESPACE.to_owned(),
598
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
508
599
  task_queue: task_q.to_owned(),
509
600
  schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
510
601
  start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
@@ -525,7 +616,7 @@ pub fn schedule_local_activity_cmd(
525
616
  ScheduleLocalActivity {
526
617
  seq,
527
618
  activity_id: activity_id.to_string(),
528
- activity_type: "test_activity".to_string(),
619
+ activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
529
620
  schedule_to_start_timeout: Some(activity_timeout.try_into().expect("duration fits")),
530
621
  start_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
531
622
  schedule_to_close_timeout: Some(activity_timeout.try_into().expect("duration fits")),
@@ -596,3 +687,23 @@ where
596
687
  .unwrap();
597
688
  }
598
689
  }
690
+
691
+ /// Initiate shutdown, drain the pollers, and wait for shutdown to complete.
692
+ pub async fn drain_pollers_and_shutdown(worker: &Arc<dyn CoreWorker>) {
693
+ worker.initiate_shutdown();
694
+ tokio::join!(
695
+ async {
696
+ assert!(matches!(
697
+ worker.poll_activity_task().await.unwrap_err(),
698
+ PollActivityError::ShutDown
699
+ ));
700
+ },
701
+ async {
702
+ assert!(matches!(
703
+ worker.poll_workflow_activation().await.unwrap_err(),
704
+ PollWfError::ShutDown,
705
+ ));
706
+ }
707
+ );
708
+ worker.shutdown().await;
709
+ }
@@ -0,0 +1,50 @@
1
+ use anyhow::anyhow;
2
+ use bytes::BytesMut;
3
+ use futures::{stream::BoxStream, SinkExt, StreamExt};
4
+ use prost::bytes::Bytes;
5
+ use std::path::Path;
6
+ use temporal_sdk_core_api::worker::WorkerConfig;
7
+ use tokio::{fs::File, sync::mpsc::UnboundedReceiver};
8
+ use tokio_util::codec::{FramedRead, FramedWrite, LengthDelimitedCodec};
9
+
10
+ pub struct WFStateReplayData {
11
+ pub config: WorkerConfig,
12
+ pub inputs: BoxStream<'static, Result<BytesMut, std::io::Error>>,
13
+ }
14
+
15
+ pub async fn stream_to_file(
16
+ config: &WorkerConfig,
17
+ mut rcv: UnboundedReceiver<Vec<u8>>,
18
+ ) -> Result<(), anyhow::Error> {
19
+ let file = File::create("wf_inputs").await?;
20
+ let mut transport = FramedWrite::new(file, ldc());
21
+ // First write the worker config, since things like cache size affect how many evictions there
22
+ // will be, etc.
23
+ transport.send(rmp_serde::to_vec(config)?.into()).await?;
24
+ while let Some(v) = rcv.recv().await {
25
+ transport.send(Bytes::from(v)).await?;
26
+ }
27
+ Ok(())
28
+ }
29
+
30
+ pub async fn read_from_file(path: impl AsRef<Path>) -> Result<WFStateReplayData, anyhow::Error> {
31
+ let file = File::open(path).await?;
32
+ let mut framed_read = FramedRead::new(file, ldc());
33
+ // Deserialize the worker config first
34
+ let config = framed_read
35
+ .next()
36
+ .await
37
+ .ok_or_else(|| anyhow!("Replay data file is empty"))??;
38
+ let config = rmp_serde::from_slice(config.as_ref())?;
39
+
40
+ Ok(WFStateReplayData {
41
+ config,
42
+ inputs: framed_read.boxed(),
43
+ })
44
+ }
45
+
46
+ fn ldc() -> LengthDelimitedCodec {
47
+ LengthDelimitedCodec::builder()
48
+ .length_field_type::<u16>()
49
+ .new_codec()
50
+ }
@@ -0,0 +1,29 @@
1
+ use crate::prost_dur;
2
+ use std::time::Duration;
3
+ use temporal_sdk::{ActivityOptions, LocalActivityOptions, WfContext, WorkflowResult};
4
+ use temporal_sdk_core_protos::{coresdk::AsJsonPayloadExt, temporal::api::common::v1::RetryPolicy};
5
+
6
+ pub async fn la_problem_workflow(ctx: WfContext) -> WorkflowResult<()> {
7
+ ctx.local_activity(LocalActivityOptions {
8
+ activity_type: "delay".to_string(),
9
+ input: "hi".as_json_payload().expect("serializes fine"),
10
+ retry_policy: RetryPolicy {
11
+ initial_interval: Some(prost_dur!(from_micros(15))),
12
+ backoff_coefficient: 1_000.,
13
+ maximum_interval: Some(prost_dur!(from_millis(1500))),
14
+ maximum_attempts: 4,
15
+ non_retryable_error_types: vec![],
16
+ },
17
+ timer_backoff_threshold: Some(Duration::from_secs(1)),
18
+ ..Default::default()
19
+ })
20
+ .await;
21
+ ctx.activity(ActivityOptions {
22
+ activity_type: "delay".to_string(),
23
+ start_to_close_timeout: Some(Duration::from_secs(20)),
24
+ input: "hi!".as_json_payload().expect("serializes fine"),
25
+ ..Default::default()
26
+ })
27
+ .await;
28
+ Ok(().into())
29
+ }
@@ -0,0 +1,130 @@
1
+ use futures_util::{sink, stream::FuturesUnordered, FutureExt, StreamExt};
2
+ use rand::{prelude::Distribution, rngs::SmallRng, Rng, SeedableRng};
3
+ use std::{future, time::Duration};
4
+ use temporal_client::{WfClientExt, WorkflowClientTrait, WorkflowOptions};
5
+ use temporal_sdk::{ActContext, ActivityOptions, LocalActivityOptions, WfContext, WorkflowResult};
6
+ use temporal_sdk_core_protos::coresdk::{AsJsonPayloadExt, FromJsonPayloadExt, IntoPayloadsExt};
7
+ use temporal_sdk_core_test_utils::CoreWfStarter;
8
+ use tokio_util::sync::CancellationToken;
9
+
10
+ const FUZZY_SIG: &str = "fuzzy_sig";
11
+
12
+ #[derive(serde::Serialize, serde::Deserialize, Copy, Clone)]
13
+ enum FuzzyWfAction {
14
+ Shutdown,
15
+ DoAct,
16
+ DoLocalAct,
17
+ }
18
+
19
+ struct FuzzyWfActionSampler;
20
+ impl Distribution<FuzzyWfAction> for FuzzyWfActionSampler {
21
+ fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> FuzzyWfAction {
22
+ let v: u8 = rng.gen_range(1..=2);
23
+ match v {
24
+ 1 => FuzzyWfAction::DoAct,
25
+ 2 => FuzzyWfAction::DoLocalAct,
26
+ _ => unreachable!(),
27
+ }
28
+ }
29
+ }
30
+
31
+ async fn echo(_ctx: ActContext, echo_me: String) -> Result<String, anyhow::Error> {
32
+ Ok(echo_me)
33
+ }
34
+
35
+ async fn fuzzy_wf_def(ctx: WfContext) -> WorkflowResult<()> {
36
+ let sigchan = ctx
37
+ .make_signal_channel(FUZZY_SIG)
38
+ .map(|sd| FuzzyWfAction::from_json_payload(&sd.input[0]).expect("Can deserialize signal"));
39
+ let done = CancellationToken::new();
40
+ let done_setter = done.clone();
41
+
42
+ sigchan
43
+ .take_until(done.cancelled())
44
+ .for_each_concurrent(None, |action| {
45
+ let fut = match action {
46
+ FuzzyWfAction::DoAct => ctx
47
+ .activity(ActivityOptions {
48
+ activity_type: "echo_activity".to_string(),
49
+ start_to_close_timeout: Some(Duration::from_secs(5)),
50
+ input: "hi!".as_json_payload().expect("serializes fine"),
51
+ ..Default::default()
52
+ })
53
+ .map(|_| ())
54
+ .boxed(),
55
+ FuzzyWfAction::DoLocalAct => ctx
56
+ .local_activity(LocalActivityOptions {
57
+ activity_type: "echo_activity".to_string(),
58
+ start_to_close_timeout: Some(Duration::from_secs(5)),
59
+ input: "hi!".as_json_payload().expect("serializes fine"),
60
+ ..Default::default()
61
+ })
62
+ .map(|_| ())
63
+ .boxed(),
64
+ FuzzyWfAction::Shutdown => {
65
+ done_setter.cancel();
66
+ future::ready(()).boxed()
67
+ }
68
+ };
69
+ fut
70
+ })
71
+ .await;
72
+
73
+ Ok(().into())
74
+ }
75
+
76
+ #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
77
+ async fn fuzzy_workflow() {
78
+ let num_workflows = 200;
79
+ let wf_name = "fuzzy_wf";
80
+ let mut starter = CoreWfStarter::new("fuzzy_workflow");
81
+ starter.max_wft(25).max_cached_workflows(25).max_at(25);
82
+ // .enable_wf_state_input_recording();
83
+ let mut worker = starter.worker().await;
84
+ worker.register_wf(wf_name.to_owned(), fuzzy_wf_def);
85
+ worker.register_activity("echo_activity", echo);
86
+ let client = starter.get_client().await;
87
+
88
+ let mut workflow_handles = vec![];
89
+ for i in 0..num_workflows {
90
+ let wfid = format!("{wf_name}_{i}");
91
+ let rid = worker
92
+ .submit_wf(
93
+ wfid.clone(),
94
+ wf_name.to_owned(),
95
+ vec![],
96
+ WorkflowOptions::default(),
97
+ )
98
+ .await
99
+ .unwrap();
100
+ workflow_handles.push(client.get_untyped_workflow_handle(wfid, rid));
101
+ }
102
+
103
+ let rng = SmallRng::seed_from_u64(523189);
104
+ let mut actions: Vec<FuzzyWfAction> = rng.sample_iter(FuzzyWfActionSampler).take(15).collect();
105
+ actions.push(FuzzyWfAction::Shutdown);
106
+
107
+ let sig_sender = async {
108
+ for action in actions {
109
+ let sends: FuturesUnordered<_> = (0..num_workflows)
110
+ .map(|i| {
111
+ client.signal_workflow_execution(
112
+ format!("{wf_name}_{i}"),
113
+ "".to_string(),
114
+ FUZZY_SIG.to_string(),
115
+ [action.as_json_payload().expect("Serializes ok")].into_payloads(),
116
+ None,
117
+ )
118
+ })
119
+ .collect();
120
+ sends
121
+ .map(|_| Ok(()))
122
+ .forward(sink::drain())
123
+ .await
124
+ .expect("Sending signals works");
125
+ tokio::time::sleep(Duration::from_secs(1)).await;
126
+ }
127
+ };
128
+ let (r1, _) = tokio::join!(worker.run_until_done(), sig_sender);
129
+ r1.unwrap();
130
+ }