temporalio 0.0.1

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 (317) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +20 -0
  4. data/README.md +130 -0
  5. data/bridge/Cargo.lock +2865 -0
  6. data/bridge/Cargo.toml +26 -0
  7. data/bridge/sdk-core/ARCHITECTURE.md +76 -0
  8. data/bridge/sdk-core/Cargo.lock +2606 -0
  9. data/bridge/sdk-core/Cargo.toml +2 -0
  10. data/bridge/sdk-core/LICENSE.txt +23 -0
  11. data/bridge/sdk-core/README.md +107 -0
  12. data/bridge/sdk-core/arch_docs/diagrams/README.md +10 -0
  13. data/bridge/sdk-core/arch_docs/diagrams/sticky_queues.puml +40 -0
  14. data/bridge/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
  15. data/bridge/sdk-core/arch_docs/sticky_queues.md +51 -0
  16. data/bridge/sdk-core/bridge-ffi/Cargo.toml +24 -0
  17. data/bridge/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  18. data/bridge/sdk-core/bridge-ffi/build.rs +25 -0
  19. data/bridge/sdk-core/bridge-ffi/include/sdk-core-bridge.h +249 -0
  20. data/bridge/sdk-core/bridge-ffi/src/lib.rs +825 -0
  21. data/bridge/sdk-core/bridge-ffi/src/wrappers.rs +211 -0
  22. data/bridge/sdk-core/client/Cargo.toml +40 -0
  23. data/bridge/sdk-core/client/LICENSE.txt +23 -0
  24. data/bridge/sdk-core/client/src/lib.rs +1294 -0
  25. data/bridge/sdk-core/client/src/metrics.rs +165 -0
  26. data/bridge/sdk-core/client/src/raw.rs +931 -0
  27. data/bridge/sdk-core/client/src/retry.rs +674 -0
  28. data/bridge/sdk-core/client/src/workflow_handle/mod.rs +185 -0
  29. data/bridge/sdk-core/core/Cargo.toml +116 -0
  30. data/bridge/sdk-core/core/LICENSE.txt +23 -0
  31. data/bridge/sdk-core/core/benches/workflow_replay.rs +73 -0
  32. data/bridge/sdk-core/core/src/abstractions.rs +166 -0
  33. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +911 -0
  34. data/bridge/sdk-core/core/src/core_tests/child_workflows.rs +221 -0
  35. data/bridge/sdk-core/core/src/core_tests/determinism.rs +107 -0
  36. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +515 -0
  37. data/bridge/sdk-core/core/src/core_tests/mod.rs +100 -0
  38. data/bridge/sdk-core/core/src/core_tests/queries.rs +736 -0
  39. data/bridge/sdk-core/core/src/core_tests/replay_flag.rs +65 -0
  40. data/bridge/sdk-core/core/src/core_tests/workers.rs +259 -0
  41. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +124 -0
  42. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +2070 -0
  43. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  44. data/bridge/sdk-core/core/src/lib.rs +175 -0
  45. data/bridge/sdk-core/core/src/log_export.rs +62 -0
  46. data/bridge/sdk-core/core/src/pollers/mod.rs +54 -0
  47. data/bridge/sdk-core/core/src/pollers/poll_buffer.rs +297 -0
  48. data/bridge/sdk-core/core/src/protosext/mod.rs +428 -0
  49. data/bridge/sdk-core/core/src/replay/mod.rs +71 -0
  50. data/bridge/sdk-core/core/src/retry_logic.rs +202 -0
  51. data/bridge/sdk-core/core/src/telemetry/metrics.rs +383 -0
  52. data/bridge/sdk-core/core/src/telemetry/mod.rs +412 -0
  53. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +77 -0
  54. data/bridge/sdk-core/core/src/test_help/mod.rs +875 -0
  55. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +580 -0
  56. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +1042 -0
  57. data/bridge/sdk-core/core/src/worker/activities.rs +464 -0
  58. data/bridge/sdk-core/core/src/worker/client/mocks.rs +87 -0
  59. data/bridge/sdk-core/core/src/worker/client.rs +347 -0
  60. data/bridge/sdk-core/core/src/worker/mod.rs +566 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +37 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +110 -0
  63. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +458 -0
  64. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +911 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +298 -0
  66. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +171 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +860 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +140 -0
  69. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +161 -0
  70. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +133 -0
  71. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +1448 -0
  72. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +342 -0
  73. data/bridge/sdk-core/core/src/worker/workflow/machines/mutable_side_effect_state_machine.rs +127 -0
  74. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +712 -0
  75. data/bridge/sdk-core/core/src/worker/workflow/machines/side_effect_state_machine.rs +71 -0
  76. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +443 -0
  77. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +439 -0
  78. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +169 -0
  79. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +246 -0
  80. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +96 -0
  81. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +1184 -0
  82. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +277 -0
  83. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  84. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +647 -0
  85. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +1143 -0
  86. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +145 -0
  87. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  88. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +940 -0
  89. data/bridge/sdk-core/core-api/Cargo.toml +31 -0
  90. data/bridge/sdk-core/core-api/LICENSE.txt +23 -0
  91. data/bridge/sdk-core/core-api/src/errors.rs +95 -0
  92. data/bridge/sdk-core/core-api/src/lib.rs +151 -0
  93. data/bridge/sdk-core/core-api/src/worker.rs +135 -0
  94. data/bridge/sdk-core/etc/deps.svg +187 -0
  95. data/bridge/sdk-core/etc/dynamic-config.yaml +2 -0
  96. data/bridge/sdk-core/etc/otel-collector-config.yaml +36 -0
  97. data/bridge/sdk-core/etc/prometheus.yaml +6 -0
  98. data/bridge/sdk-core/fsm/Cargo.toml +18 -0
  99. data/bridge/sdk-core/fsm/LICENSE.txt +23 -0
  100. data/bridge/sdk-core/fsm/README.md +3 -0
  101. data/bridge/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +27 -0
  102. data/bridge/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +23 -0
  103. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +647 -0
  104. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/progress.rs +8 -0
  105. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.rs +18 -0
  106. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +12 -0
  107. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dynamic_dest_pass.rs +41 -0
  108. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.rs +14 -0
  109. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.stderr +11 -0
  110. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_arg_pass.rs +32 -0
  111. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_pass.rs +31 -0
  112. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/medium_complex_pass.rs +46 -0
  113. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.rs +29 -0
  114. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +12 -0
  115. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/simple_pass.rs +32 -0
  116. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.rs +18 -0
  117. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.stderr +5 -0
  118. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.rs +11 -0
  119. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.stderr +5 -0
  120. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.rs +11 -0
  121. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.stderr +5 -0
  122. data/bridge/sdk-core/fsm/rustfsm_trait/Cargo.toml +14 -0
  123. data/bridge/sdk-core/fsm/rustfsm_trait/LICENSE.txt +23 -0
  124. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +249 -0
  125. data/bridge/sdk-core/fsm/src/lib.rs +2 -0
  126. data/bridge/sdk-core/histories/fail_wf_task.bin +0 -0
  127. data/bridge/sdk-core/histories/timer_workflow_history.bin +0 -0
  128. data/bridge/sdk-core/integ-with-otel.sh +7 -0
  129. data/bridge/sdk-core/protos/api_upstream/README.md +9 -0
  130. data/bridge/sdk-core/protos/api_upstream/api-linter.yaml +40 -0
  131. data/bridge/sdk-core/protos/api_upstream/buf.yaml +12 -0
  132. data/bridge/sdk-core/protos/api_upstream/dependencies/gogoproto/gogo.proto +141 -0
  133. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +86 -0
  134. data/bridge/sdk-core/protos/api_upstream/temporal/api/cluster/v1/message.proto +83 -0
  135. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +259 -0
  136. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +112 -0
  137. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +46 -0
  138. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/cluster.proto +40 -0
  139. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +57 -0
  140. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +55 -0
  141. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +168 -0
  142. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +97 -0
  143. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +51 -0
  144. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +50 -0
  145. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +41 -0
  146. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
  147. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +59 -0
  148. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +51 -0
  149. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +122 -0
  150. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +108 -0
  151. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +114 -0
  152. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +56 -0
  153. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +751 -0
  154. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +97 -0
  155. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +161 -0
  156. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +99 -0
  157. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +61 -0
  158. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +55 -0
  159. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +300 -0
  160. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +108 -0
  161. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +46 -0
  162. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +59 -0
  163. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +145 -0
  164. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +1124 -0
  165. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +401 -0
  166. data/bridge/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  167. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  168. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +79 -0
  169. data/bridge/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +210 -0
  170. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +77 -0
  171. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +15 -0
  172. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +30 -0
  173. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  174. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +261 -0
  175. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +297 -0
  176. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +29 -0
  177. data/bridge/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  178. data/bridge/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  179. data/bridge/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  180. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  181. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  182. data/bridge/sdk-core/rustfmt.toml +1 -0
  183. data/bridge/sdk-core/sdk/Cargo.toml +47 -0
  184. data/bridge/sdk-core/sdk/LICENSE.txt +23 -0
  185. data/bridge/sdk-core/sdk/src/activity_context.rs +230 -0
  186. data/bridge/sdk-core/sdk/src/app_data.rs +37 -0
  187. data/bridge/sdk-core/sdk/src/conversions.rs +8 -0
  188. data/bridge/sdk-core/sdk/src/interceptors.rs +17 -0
  189. data/bridge/sdk-core/sdk/src/lib.rs +792 -0
  190. data/bridge/sdk-core/sdk/src/payload_converter.rs +11 -0
  191. data/bridge/sdk-core/sdk/src/workflow_context/options.rs +295 -0
  192. data/bridge/sdk-core/sdk/src/workflow_context.rs +683 -0
  193. data/bridge/sdk-core/sdk/src/workflow_future.rs +503 -0
  194. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +30 -0
  195. data/bridge/sdk-core/sdk-core-protos/LICENSE.txt +23 -0
  196. data/bridge/sdk-core/sdk-core-protos/build.rs +108 -0
  197. data/bridge/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  198. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +497 -0
  199. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +230 -0
  200. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +1910 -0
  201. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  202. data/bridge/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  203. data/bridge/sdk-core/test-utils/Cargo.toml +35 -0
  204. data/bridge/sdk-core/test-utils/src/canned_histories.rs +1579 -0
  205. data/bridge/sdk-core/test-utils/src/histfetch.rs +28 -0
  206. data/bridge/sdk-core/test-utils/src/lib.rs +598 -0
  207. data/bridge/sdk-core/tests/integ_tests/client_tests.rs +36 -0
  208. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  209. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +218 -0
  210. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +146 -0
  211. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +437 -0
  212. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  213. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +878 -0
  214. data/bridge/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  215. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +59 -0
  216. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +58 -0
  217. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +50 -0
  218. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +60 -0
  219. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +54 -0
  220. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +634 -0
  221. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +113 -0
  222. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +137 -0
  223. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +93 -0
  224. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +167 -0
  225. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +99 -0
  226. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +131 -0
  227. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +75 -0
  228. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +587 -0
  229. data/bridge/sdk-core/tests/load_tests.rs +191 -0
  230. data/bridge/sdk-core/tests/main.rs +111 -0
  231. data/bridge/sdk-core/tests/runner.rs +93 -0
  232. data/bridge/src/connection.rs +167 -0
  233. data/bridge/src/lib.rs +180 -0
  234. data/bridge/src/runtime.rs +47 -0
  235. data/bridge/src/worker.rs +73 -0
  236. data/ext/Rakefile +9 -0
  237. data/lib/bridge.so +0 -0
  238. data/lib/gen/dependencies/gogoproto/gogo_pb.rb +14 -0
  239. data/lib/gen/temporal/api/batch/v1/message_pb.rb +48 -0
  240. data/lib/gen/temporal/api/cluster/v1/message_pb.rb +67 -0
  241. data/lib/gen/temporal/api/command/v1/message_pb.rb +166 -0
  242. data/lib/gen/temporal/api/common/v1/message_pb.rb +69 -0
  243. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +32 -0
  244. data/lib/gen/temporal/api/enums/v1/cluster_pb.rb +26 -0
  245. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +37 -0
  246. data/lib/gen/temporal/api/enums/v1/common_pb.rb +41 -0
  247. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +67 -0
  248. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +71 -0
  249. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +37 -0
  250. data/lib/gen/temporal/api/enums/v1/query_pb.rb +31 -0
  251. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +24 -0
  252. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +28 -0
  253. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +30 -0
  254. data/lib/gen/temporal/api/enums/v1/update_pb.rb +28 -0
  255. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +89 -0
  256. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +84 -0
  257. data/lib/gen/temporal/api/failure/v1/message_pb.rb +83 -0
  258. data/lib/gen/temporal/api/filter/v1/message_pb.rb +40 -0
  259. data/lib/gen/temporal/api/history/v1/message_pb.rb +489 -0
  260. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +63 -0
  261. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +125 -0
  262. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +20 -0
  263. data/lib/gen/temporal/api/query/v1/message_pb.rb +38 -0
  264. data/lib/gen/temporal/api/replication/v1/message_pb.rb +37 -0
  265. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +128 -0
  266. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +73 -0
  267. data/lib/gen/temporal/api/update/v1/message_pb.rb +26 -0
  268. data/lib/gen/temporal/api/version/v1/message_pb.rb +41 -0
  269. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +110 -0
  270. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +771 -0
  271. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +20 -0
  272. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +58 -0
  273. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +57 -0
  274. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +222 -0
  275. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +57 -0
  276. data/lib/gen/temporal/sdk/core/common/common_pb.rb +22 -0
  277. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +34 -0
  278. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +27 -0
  279. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +164 -0
  280. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +192 -0
  281. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +34 -0
  282. data/lib/temporal/bridge.rb +14 -0
  283. data/lib/temporal/client/implementation.rb +339 -0
  284. data/lib/temporal/client/workflow_handle.rb +243 -0
  285. data/lib/temporal/client.rb +144 -0
  286. data/lib/temporal/connection.rb +736 -0
  287. data/lib/temporal/data_converter.rb +150 -0
  288. data/lib/temporal/error/failure.rb +194 -0
  289. data/lib/temporal/error/workflow_failure.rb +17 -0
  290. data/lib/temporal/errors.rb +22 -0
  291. data/lib/temporal/failure_converter/base.rb +26 -0
  292. data/lib/temporal/failure_converter/basic.rb +313 -0
  293. data/lib/temporal/failure_converter.rb +8 -0
  294. data/lib/temporal/interceptor/chain.rb +27 -0
  295. data/lib/temporal/interceptor/client.rb +102 -0
  296. data/lib/temporal/payload_codec/base.rb +32 -0
  297. data/lib/temporal/payload_converter/base.rb +24 -0
  298. data/lib/temporal/payload_converter/bytes.rb +26 -0
  299. data/lib/temporal/payload_converter/composite.rb +47 -0
  300. data/lib/temporal/payload_converter/encoding_base.rb +35 -0
  301. data/lib/temporal/payload_converter/json.rb +25 -0
  302. data/lib/temporal/payload_converter/nil.rb +25 -0
  303. data/lib/temporal/payload_converter.rb +14 -0
  304. data/lib/temporal/retry_policy.rb +82 -0
  305. data/lib/temporal/retry_state.rb +35 -0
  306. data/lib/temporal/runtime.rb +22 -0
  307. data/lib/temporal/timeout_type.rb +29 -0
  308. data/lib/temporal/version.rb +3 -0
  309. data/lib/temporal/workflow/execution_info.rb +54 -0
  310. data/lib/temporal/workflow/execution_status.rb +36 -0
  311. data/lib/temporal/workflow/id_reuse_policy.rb +36 -0
  312. data/lib/temporal/workflow/query_reject_condition.rb +33 -0
  313. data/lib/temporal.rb +8 -0
  314. data/lib/temporalio.rb +3 -0
  315. data/lib/thermite_patch.rb +23 -0
  316. data/temporalio.gemspec +41 -0
  317. metadata +583 -0
@@ -0,0 +1,428 @@
1
+ use crate::{
2
+ worker::{LocalActivityExecutionResult, LEGACY_QUERY_ID},
3
+ CompleteActivityError, TaskToken,
4
+ };
5
+ use anyhow::anyhow;
6
+ use std::{
7
+ collections::HashMap,
8
+ convert::TryFrom,
9
+ fmt::{Debug, Display, Formatter},
10
+ time::{Duration, SystemTime},
11
+ };
12
+ use temporal_sdk_core_protos::{
13
+ constants::{LOCAL_ACTIVITY_MARKER_NAME, PATCH_MARKER_NAME},
14
+ coresdk::{
15
+ activity_result::{activity_execution_result, activity_execution_result::Status},
16
+ common::{
17
+ decode_change_marker_details, extract_local_activity_marker_data,
18
+ extract_local_activity_marker_details,
19
+ },
20
+ external_data::LocalActivityMarkerData,
21
+ workflow_activation::{
22
+ query_to_job, workflow_activation_job, QueryWorkflow, WorkflowActivation,
23
+ WorkflowActivationJob,
24
+ },
25
+ workflow_commands::{
26
+ query_result, ActivityCancellationType, QueryResult, ScheduleLocalActivity,
27
+ },
28
+ workflow_completion,
29
+ },
30
+ temporal::api::{
31
+ common::v1::{Payload, RetryPolicy, WorkflowExecution},
32
+ enums::v1::EventType,
33
+ failure::v1::Failure,
34
+ history::v1::{history_event, History, HistoryEvent, MarkerRecordedEventAttributes},
35
+ query::v1::WorkflowQuery,
36
+ workflowservice::v1::PollWorkflowTaskQueueResponse,
37
+ },
38
+ utilities::TryIntoOrNone,
39
+ };
40
+
41
+ /// A validated version of a [PollWorkflowTaskQueueResponse]
42
+ #[derive(Clone, PartialEq)]
43
+ #[allow(clippy::manual_non_exhaustive)] // Clippy doesn't understand it's only for *in* this crate
44
+ pub struct ValidPollWFTQResponse {
45
+ pub task_token: TaskToken,
46
+ pub task_queue: String,
47
+ pub workflow_execution: WorkflowExecution,
48
+ pub workflow_type: String,
49
+ pub history: History,
50
+ pub next_page_token: Vec<u8>,
51
+ pub attempt: u32,
52
+ pub previous_started_event_id: i64,
53
+ pub started_event_id: i64,
54
+ /// If this is present, `history` will be empty. This is not a very "tight" design, but it's
55
+ /// enforced at construction time. From the `query` field.
56
+ pub legacy_query: Option<WorkflowQuery>,
57
+ /// Query requests from the `queries` field
58
+ pub query_requests: Vec<QueryWorkflow>,
59
+
60
+ /// Zero-size field to prevent explicit construction
61
+ _cant_construct_me: (),
62
+ }
63
+
64
+ impl Debug for ValidPollWFTQResponse {
65
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
66
+ write!(
67
+ f,
68
+ "ValidWFT {{ task_token: {}, task_queue: {}, workflow_execution: {:?}, \
69
+ workflow_type: {}, attempt: {}, previous_started_event_id: {}, started_event_id {}, \
70
+ history_length: {}, first_evt_in_hist_id: {:?}, legacy_query: {:?}, queries: {:?} }}",
71
+ self.task_token,
72
+ self.task_queue,
73
+ self.workflow_execution,
74
+ self.workflow_type,
75
+ self.attempt,
76
+ self.previous_started_event_id,
77
+ self.started_event_id,
78
+ self.history.events.len(),
79
+ self.history.events.get(0).map(|e| e.event_id),
80
+ self.legacy_query,
81
+ self.query_requests
82
+ )
83
+ }
84
+ }
85
+
86
+ impl TryFrom<PollWorkflowTaskQueueResponse> for ValidPollWFTQResponse {
87
+ /// We return the poll response itself if it was invalid
88
+ type Error = PollWorkflowTaskQueueResponse;
89
+
90
+ fn try_from(value: PollWorkflowTaskQueueResponse) -> Result<Self, Self::Error> {
91
+ match value {
92
+ PollWorkflowTaskQueueResponse {
93
+ task_token,
94
+ workflow_execution_task_queue: Some(tq),
95
+ workflow_execution: Some(workflow_execution),
96
+ workflow_type: Some(workflow_type),
97
+ history: Some(history),
98
+ next_page_token,
99
+ attempt,
100
+ previous_started_event_id,
101
+ started_event_id,
102
+ query,
103
+ queries,
104
+ ..
105
+ } => {
106
+ let query_requests = queries
107
+ .into_iter()
108
+ .map(|(id, q)| query_to_job(id, q))
109
+ .collect();
110
+
111
+ Ok(Self {
112
+ task_token: TaskToken(task_token),
113
+ task_queue: tq.name,
114
+ workflow_execution,
115
+ workflow_type: workflow_type.name,
116
+ history,
117
+ next_page_token,
118
+ attempt: attempt as u32,
119
+ previous_started_event_id,
120
+ started_event_id,
121
+ legacy_query: query,
122
+ query_requests,
123
+ _cant_construct_me: (),
124
+ })
125
+ }
126
+ _ => Err(value),
127
+ }
128
+ }
129
+ }
130
+
131
+ pub(crate) trait WorkflowActivationExt {
132
+ /// Returns true if this activation has one and only one job to perform a legacy query
133
+ fn is_legacy_query(&self) -> bool;
134
+ }
135
+
136
+ impl WorkflowActivationExt for WorkflowActivation {
137
+ fn is_legacy_query(&self) -> bool {
138
+ matches!(&self.jobs.as_slice(), &[WorkflowActivationJob {
139
+ variant: Some(workflow_activation_job::Variant::QueryWorkflow(qr))
140
+ }] if qr.query_id == LEGACY_QUERY_ID)
141
+ }
142
+ }
143
+
144
+ /// Create a legacy query failure result
145
+ pub(crate) fn legacy_query_failure(fail: workflow_completion::Failure) -> QueryResult {
146
+ QueryResult {
147
+ query_id: LEGACY_QUERY_ID.to_string(),
148
+ variant: Some(query_result::Variant::Failed(
149
+ fail.failure.unwrap_or_default(),
150
+ )),
151
+ }
152
+ }
153
+
154
+ pub(crate) trait HistoryEventExt {
155
+ /// If this history event represents a `patched` marker, return the info about
156
+ /// it. Returns `None` if it is any other kind of event or marker.
157
+ fn get_patch_marker_details(&self) -> Option<(String, bool)>;
158
+ /// If this history event represents a local activity marker, return true.
159
+ fn is_local_activity_marker(&self) -> bool;
160
+ /// If this history event represents a local activity marker, return the marker id info.
161
+ /// Returns `None` if it is any other kind of event or marker or the data is invalid.
162
+ fn extract_local_activity_marker_data(&self) -> Option<LocalActivityMarkerData>;
163
+ /// If this history event represents a local activity marker, return all the contained data.
164
+ /// Returns `None` if it is any other kind of event or marker or the data is invalid.
165
+ fn into_local_activity_marker_details(self) -> Option<CompleteLocalActivityData>;
166
+ }
167
+
168
+ impl HistoryEventExt for HistoryEvent {
169
+ fn get_patch_marker_details(&self) -> Option<(String, bool)> {
170
+ if self.event_type() == EventType::MarkerRecorded {
171
+ match &self.attributes {
172
+ Some(history_event::Attributes::MarkerRecordedEventAttributes(
173
+ MarkerRecordedEventAttributes {
174
+ marker_name,
175
+ details,
176
+ ..
177
+ },
178
+ )) if marker_name == PATCH_MARKER_NAME => decode_change_marker_details(details),
179
+ _ => None,
180
+ }
181
+ } else {
182
+ None
183
+ }
184
+ }
185
+
186
+ fn is_local_activity_marker(&self) -> bool {
187
+ if self.event_type() == EventType::MarkerRecorded {
188
+ return matches!(&self.attributes,
189
+ Some(history_event::Attributes::MarkerRecordedEventAttributes(
190
+ MarkerRecordedEventAttributes { marker_name, .. },
191
+ )) if marker_name == LOCAL_ACTIVITY_MARKER_NAME
192
+ );
193
+ }
194
+ false
195
+ }
196
+
197
+ fn extract_local_activity_marker_data(&self) -> Option<LocalActivityMarkerData> {
198
+ if self.event_type() == EventType::MarkerRecorded {
199
+ match &self.attributes {
200
+ Some(history_event::Attributes::MarkerRecordedEventAttributes(
201
+ MarkerRecordedEventAttributes {
202
+ marker_name,
203
+ details,
204
+ ..
205
+ },
206
+ )) if marker_name == LOCAL_ACTIVITY_MARKER_NAME => {
207
+ extract_local_activity_marker_data(details)
208
+ }
209
+ _ => None,
210
+ }
211
+ } else {
212
+ None
213
+ }
214
+ }
215
+
216
+ fn into_local_activity_marker_details(self) -> Option<CompleteLocalActivityData> {
217
+ if self.event_type() == EventType::MarkerRecorded {
218
+ match self.attributes {
219
+ Some(history_event::Attributes::MarkerRecordedEventAttributes(
220
+ MarkerRecordedEventAttributes {
221
+ marker_name,
222
+ mut details,
223
+ failure,
224
+ ..
225
+ },
226
+ )) if marker_name == LOCAL_ACTIVITY_MARKER_NAME => {
227
+ let (data, ok_res) = extract_local_activity_marker_details(&mut details);
228
+ let data = data?;
229
+ let result = match (ok_res, failure) {
230
+ (Some(r), None) => Ok(r),
231
+ (None | Some(_), Some(f)) => Err(f),
232
+ (None, None) => Ok(Default::default()),
233
+ };
234
+ Some(CompleteLocalActivityData {
235
+ marker_dat: data,
236
+ result,
237
+ })
238
+ }
239
+ _ => None,
240
+ }
241
+ } else {
242
+ None
243
+ }
244
+ }
245
+ }
246
+
247
+ pub(crate) struct CompleteLocalActivityData {
248
+ pub marker_dat: LocalActivityMarkerData,
249
+ pub result: Result<Payload, Failure>,
250
+ }
251
+
252
+ pub(crate) fn validate_activity_completion(
253
+ status: &activity_execution_result::Status,
254
+ ) -> Result<(), CompleteActivityError> {
255
+ match status {
256
+ Status::Completed(c) if c.result.is_none() => {
257
+ Err(CompleteActivityError::MalformedActivityCompletion {
258
+ reason: "Activity completions must contain a `result` payload \
259
+ (which may be empty)"
260
+ .to_string(),
261
+ completion: None,
262
+ })
263
+ }
264
+ _ => Ok(()),
265
+ }
266
+ }
267
+
268
+ impl TryFrom<activity_execution_result::Status> for LocalActivityExecutionResult {
269
+ type Error = CompleteActivityError;
270
+
271
+ fn try_from(s: activity_execution_result::Status) -> Result<Self, Self::Error> {
272
+ match s {
273
+ Status::Completed(c) => Ok(LocalActivityExecutionResult::Completed(c)),
274
+ Status::Failed(f)
275
+ if f.failure
276
+ .as_ref()
277
+ .map(|fail| fail.is_timeout())
278
+ .unwrap_or_default() =>
279
+ {
280
+ Ok(LocalActivityExecutionResult::TimedOut(f))
281
+ }
282
+ Status::Failed(f) => Ok(LocalActivityExecutionResult::Failed(f)),
283
+ Status::Cancelled(cancel) => Ok(LocalActivityExecutionResult::Cancelled(cancel)),
284
+ Status::WillCompleteAsync(_) => {
285
+ Err(CompleteActivityError::MalformedActivityCompletion {
286
+ reason: "Local activities cannot be completed async".to_string(),
287
+ completion: None,
288
+ })
289
+ }
290
+ }
291
+ }
292
+ }
293
+
294
+ /// Validated version of [ScheduleLocalActivity]. See it for field docs.
295
+ /// One or both of `schedule_to_close_timeout` and `start_to_close_timeout` are guaranteed to exist.
296
+ #[derive(Debug, Clone)]
297
+ #[cfg_attr(test, derive(Default))]
298
+ pub struct ValidScheduleLA {
299
+ pub seq: u32,
300
+ pub activity_id: String,
301
+ pub activity_type: String,
302
+ pub attempt: u32,
303
+ pub original_schedule_time: Option<SystemTime>,
304
+ pub headers: HashMap<String, Payload>,
305
+ pub arguments: Vec<Payload>,
306
+ pub schedule_to_start_timeout: Option<Duration>,
307
+ pub close_timeouts: LACloseTimeouts,
308
+ pub retry_policy: RetryPolicy,
309
+ pub local_retry_threshold: Duration,
310
+ pub cancellation_type: ActivityCancellationType,
311
+ }
312
+
313
+ #[derive(Debug, Clone, Copy)]
314
+ pub enum LACloseTimeouts {
315
+ ScheduleOnly(Duration),
316
+ StartOnly(Duration),
317
+ Both { sched: Duration, start: Duration },
318
+ }
319
+
320
+ impl LACloseTimeouts {
321
+ /// Splits into (schedule_to_close, start_to_close) options, one or both of which is guaranteed
322
+ /// to be populated
323
+ pub fn into_sched_and_start(self) -> (Option<Duration>, Option<Duration>) {
324
+ match self {
325
+ LACloseTimeouts::ScheduleOnly(x) => (Some(x), None),
326
+ LACloseTimeouts::StartOnly(x) => (None, Some(x)),
327
+ LACloseTimeouts::Both { sched, start } => (Some(sched), Some(start)),
328
+ }
329
+ }
330
+ }
331
+
332
+ #[cfg(test)]
333
+ impl Default for LACloseTimeouts {
334
+ fn default() -> Self {
335
+ LACloseTimeouts::ScheduleOnly(Duration::from_secs(100))
336
+ }
337
+ }
338
+
339
+ impl ValidScheduleLA {
340
+ pub fn from_schedule_la(
341
+ v: ScheduleLocalActivity,
342
+ wf_exe_timeout: Option<Duration>,
343
+ ) -> Result<Self, anyhow::Error> {
344
+ let original_schedule_time = v
345
+ .original_schedule_time
346
+ .map(|x| {
347
+ x.try_into()
348
+ .map_err(|_| anyhow!("Could not convert original_schedule_time"))
349
+ })
350
+ .transpose()?;
351
+ let sched_to_close = v
352
+ .schedule_to_close_timeout
353
+ .map(|x| {
354
+ x.try_into()
355
+ .map_err(|_| anyhow!("Could not convert schedule_to_close_timeout"))
356
+ })
357
+ .transpose()?
358
+ // Default to execution timeout if unset
359
+ .or(wf_exe_timeout);
360
+ let mut schedule_to_start_timeout = v
361
+ .schedule_to_start_timeout
362
+ .map(|x| {
363
+ x.try_into()
364
+ .map_err(|_| anyhow!("Could not convert schedule_to_start_timeout"))
365
+ })
366
+ .transpose()?;
367
+ // Clamp schedule-to-start if larger than schedule-to-close
368
+ if let Some((sched_to_start, sched_to_close)) =
369
+ schedule_to_start_timeout.as_mut().zip(sched_to_close)
370
+ {
371
+ if *sched_to_start > sched_to_close {
372
+ *sched_to_start = sched_to_close;
373
+ }
374
+ }
375
+ let close_timeouts = match (
376
+ sched_to_close,
377
+ v.start_to_close_timeout
378
+ .map(|x| {
379
+ x.try_into()
380
+ .map_err(|_| anyhow!("Could not convert start_to_close_timeout"))
381
+ })
382
+ .transpose()?,
383
+ ) {
384
+ (Some(sch), None) => LACloseTimeouts::ScheduleOnly(sch),
385
+ (None, Some(start)) => LACloseTimeouts::StartOnly(start),
386
+ (Some(sched), Some(mut start)) => {
387
+ // Clamp start-to-close if larger than schedule-to-close
388
+ if start > sched {
389
+ start = sched;
390
+ }
391
+ LACloseTimeouts::Both { sched, start }
392
+ }
393
+ (None, None) => {
394
+ return Err(anyhow!(
395
+ "One of schedule_to_close or start_to_close timeouts must be set"
396
+ ))
397
+ }
398
+ };
399
+ let retry_policy = v.retry_policy.unwrap_or_default();
400
+ let local_retry_threshold = v
401
+ .local_retry_threshold
402
+ .clone()
403
+ .try_into_or_none()
404
+ .unwrap_or_else(|| Duration::from_secs(60));
405
+ let cancellation_type = ActivityCancellationType::from_i32(v.cancellation_type)
406
+ .unwrap_or(ActivityCancellationType::WaitCancellationCompleted);
407
+ Ok(ValidScheduleLA {
408
+ seq: v.seq,
409
+ activity_id: v.activity_id,
410
+ activity_type: v.activity_type,
411
+ attempt: v.attempt,
412
+ original_schedule_time,
413
+ headers: v.headers,
414
+ arguments: v.arguments,
415
+ schedule_to_start_timeout,
416
+ close_timeouts,
417
+ retry_policy,
418
+ local_retry_threshold,
419
+ cancellation_type,
420
+ })
421
+ }
422
+ }
423
+
424
+ impl Display for ValidScheduleLA {
425
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
426
+ write!(f, "ValidScheduleLA({}, {})", self.seq, self.activity_type)
427
+ }
428
+ }
@@ -0,0 +1,71 @@
1
+ //! This module implements support for creating special core instances and workers which can be used
2
+ //! to replay canned histories. It should be used by Lang SDKs to provide replay capabilities to
3
+ //! users during testing.
4
+
5
+ use crate::worker::client::{mocks::mock_manual_workflow_client, WorkerClient};
6
+ use futures::FutureExt;
7
+ use std::{
8
+ sync::{
9
+ atomic::{AtomicBool, Ordering},
10
+ Arc,
11
+ },
12
+ time::Duration,
13
+ };
14
+ use temporal_sdk_core_protos::temporal::api::{
15
+ common::v1::WorkflowExecution,
16
+ history::v1::History,
17
+ workflowservice::v1::{
18
+ RespondWorkflowTaskCompletedResponse, RespondWorkflowTaskFailedResponse,
19
+ },
20
+ };
21
+ pub use temporal_sdk_core_protos::{
22
+ default_wes_attribs, HistoryInfo, TestHistoryBuilder, DEFAULT_WORKFLOW_TYPE,
23
+ };
24
+
25
+ /// Create a mock client which can be used by a replay worker to serve up canned history.
26
+ /// It will return the entire history in one workflow task, after that it will return default
27
+ /// responses (with a 10s wait). If a workflow task failure is sent to the mock, it will send
28
+ /// the complete response again.
29
+ pub(crate) fn mock_client_from_history(
30
+ history: &History,
31
+ task_queue: impl Into<String>,
32
+ ) -> impl WorkerClient {
33
+ let mut mg = mock_manual_workflow_client();
34
+
35
+ let hist_info = HistoryInfo::new_from_history(history, None).unwrap();
36
+ let wf = WorkflowExecution {
37
+ workflow_id: "fake_wf_id".to_string(),
38
+ run_id: hist_info.orig_run_id().to_string(),
39
+ };
40
+
41
+ let did_send = Arc::new(AtomicBool::new(false));
42
+ let did_send_clone = did_send.clone();
43
+ let tq = task_queue.into();
44
+ mg.expect_poll_workflow_task().returning(move |_, _| {
45
+ let hist_info = hist_info.clone();
46
+ let wf = wf.clone();
47
+ let did_send_clone = did_send_clone.clone();
48
+ let tq = tq.clone();
49
+ async move {
50
+ if !did_send_clone.swap(true, Ordering::AcqRel) {
51
+ let mut resp = hist_info.as_poll_wft_response(tq);
52
+ resp.workflow_execution = Some(wf.clone());
53
+ Ok(resp)
54
+ } else {
55
+ tokio::time::sleep(Duration::from_secs(10)).await;
56
+ Ok(Default::default())
57
+ }
58
+ }
59
+ .boxed()
60
+ });
61
+
62
+ mg.expect_complete_workflow_task()
63
+ .returning(|_| async move { Ok(RespondWorkflowTaskCompletedResponse::default()) }.boxed());
64
+ mg.expect_fail_workflow_task().returning(move |_, _, _| {
65
+ // We'll need to re-send the history if WFT fails
66
+ did_send.store(false, Ordering::Release);
67
+ async move { Ok(RespondWorkflowTaskFailedResponse {}) }.boxed()
68
+ });
69
+
70
+ mg
71
+ }
@@ -0,0 +1,202 @@
1
+ use std::time::Duration;
2
+ use temporal_sdk_core_protos::{
3
+ temporal::api::{common::v1::RetryPolicy, failure::v1::ApplicationFailureInfo},
4
+ utilities::TryIntoOrNone,
5
+ };
6
+
7
+ pub(crate) trait RetryPolicyExt {
8
+ /// Ask this retry policy if a retry should be performed. Caller provides the current attempt
9
+ /// number - the first attempt should start at 1.
10
+ ///
11
+ /// Returns `None` if it should not, otherwise a duration indicating how long to wait before
12
+ /// performing the retry.
13
+ ///
14
+ /// Applies defaults to missing fields:
15
+ /// `initial_interval` - 1 second
16
+ /// `maximum_interval` - 100 x initial_interval
17
+ /// `backoff_coefficient` - 2.0
18
+ fn should_retry(
19
+ &self,
20
+ attempt_number: usize,
21
+ application_failure: Option<&ApplicationFailureInfo>,
22
+ ) -> Option<Duration>;
23
+ }
24
+
25
+ impl RetryPolicyExt for RetryPolicy {
26
+ fn should_retry(
27
+ &self,
28
+ attempt_number: usize,
29
+ application_failure: Option<&ApplicationFailureInfo>,
30
+ ) -> Option<Duration> {
31
+ let non_retryable = application_failure
32
+ .map(|f| f.non_retryable)
33
+ .unwrap_or_default();
34
+ if non_retryable {
35
+ return None;
36
+ }
37
+ let err_type_str = application_failure.map_or("", |f| &f.r#type);
38
+ let realmax = self.maximum_attempts.max(0);
39
+ if realmax > 0 && attempt_number >= realmax as usize {
40
+ return None;
41
+ }
42
+
43
+ for pat in &self.non_retryable_error_types {
44
+ if err_type_str.to_lowercase() == pat.to_lowercase() {
45
+ return None;
46
+ }
47
+ }
48
+
49
+ let converted_interval = self
50
+ .initial_interval
51
+ .clone()
52
+ .try_into_or_none()
53
+ .or(Some(Duration::from_secs(1)));
54
+ if attempt_number == 1 {
55
+ return converted_interval;
56
+ }
57
+ let coeff = if self.backoff_coefficient != 0. {
58
+ self.backoff_coefficient
59
+ } else {
60
+ 2.0
61
+ };
62
+
63
+ if let Some(interval) = converted_interval {
64
+ let max_iv = self
65
+ .maximum_interval
66
+ .clone()
67
+ .try_into_or_none()
68
+ .unwrap_or_else(|| interval.saturating_mul(100));
69
+ let mul_factor = coeff.powi(attempt_number as i32 - 1);
70
+ let tried_mul = try_from_secs_f64(mul_factor * interval.as_secs_f64());
71
+ Some(tried_mul.unwrap_or(max_iv).min(max_iv))
72
+ } else {
73
+ // No retries if initial interval is not specified
74
+ None
75
+ }
76
+ }
77
+ }
78
+
79
+ const NANOS_PER_SEC: u32 = 1_000_000_000;
80
+ /// modified from rust stdlib since this feature is currently nightly only
81
+ fn try_from_secs_f64(secs: f64) -> Option<Duration> {
82
+ const MAX_NANOS_F64: f64 = ((u64::MAX as u128 + 1) * (NANOS_PER_SEC as u128)) as f64;
83
+ let nanos = secs * (NANOS_PER_SEC as f64);
84
+ if !nanos.is_finite() || !(0.0..MAX_NANOS_F64).contains(&nanos) {
85
+ None
86
+ } else {
87
+ Some(Duration::from_secs_f64(secs))
88
+ }
89
+ }
90
+
91
+ #[cfg(test)]
92
+ mod tests {
93
+ use super::*;
94
+ use crate::prost_dur;
95
+
96
+ #[test]
97
+ fn calcs_backoffs_properly() {
98
+ let rp = RetryPolicy {
99
+ initial_interval: Some(prost_dur!(from_secs(1))),
100
+ backoff_coefficient: 2.0,
101
+ maximum_interval: Some(prost_dur!(from_secs(10))),
102
+ maximum_attempts: 10,
103
+ non_retryable_error_types: vec![],
104
+ };
105
+ let res = rp.should_retry(1, None).unwrap();
106
+ assert_eq!(res.as_millis(), 1_000);
107
+ let res = rp.should_retry(2, None).unwrap();
108
+ assert_eq!(res.as_millis(), 2_000);
109
+ let res = rp.should_retry(3, None).unwrap();
110
+ assert_eq!(res.as_millis(), 4_000);
111
+ let res = rp.should_retry(4, None).unwrap();
112
+ assert_eq!(res.as_millis(), 8_000);
113
+ let res = rp.should_retry(5, None).unwrap();
114
+ assert_eq!(res.as_millis(), 10_000);
115
+ let res = rp.should_retry(6, None).unwrap();
116
+ assert_eq!(res.as_millis(), 10_000);
117
+ // Max attempts - no retry
118
+ assert!(rp.should_retry(10, None).is_none());
119
+ }
120
+
121
+ #[test]
122
+ fn no_interval_no_backoff() {
123
+ let rp = RetryPolicy {
124
+ initial_interval: None,
125
+ backoff_coefficient: 0.,
126
+ maximum_interval: None,
127
+ maximum_attempts: 10,
128
+ non_retryable_error_types: vec![],
129
+ };
130
+ assert!(rp.should_retry(1, None).is_some());
131
+ }
132
+
133
+ #[test]
134
+ fn max_attempts_zero_retry_forever() {
135
+ let rp = RetryPolicy {
136
+ initial_interval: Some(prost_dur!(from_secs(1))),
137
+ backoff_coefficient: 1.2,
138
+ maximum_interval: None,
139
+ maximum_attempts: 0,
140
+ non_retryable_error_types: vec![],
141
+ };
142
+ for i in 0..50 {
143
+ assert!(rp.should_retry(i, None).is_some());
144
+ }
145
+ }
146
+
147
+ #[test]
148
+ fn no_overflows() {
149
+ let rp = RetryPolicy {
150
+ initial_interval: Some(prost_dur!(from_secs(1))),
151
+ backoff_coefficient: 10.,
152
+ maximum_interval: None,
153
+ maximum_attempts: 0,
154
+ non_retryable_error_types: vec![],
155
+ };
156
+ for i in 0..50 {
157
+ assert!(rp.should_retry(i, None).is_some());
158
+ }
159
+ }
160
+
161
+ #[test]
162
+ fn no_retry_err_str_match() {
163
+ let rp = RetryPolicy {
164
+ initial_interval: Some(prost_dur!(from_secs(1))),
165
+ backoff_coefficient: 2.0,
166
+ maximum_interval: Some(prost_dur!(from_secs(10))),
167
+ maximum_attempts: 10,
168
+ non_retryable_error_types: vec!["no retry".to_string()],
169
+ };
170
+ assert!(rp
171
+ .should_retry(
172
+ 1,
173
+ Some(&ApplicationFailureInfo {
174
+ r#type: "no retry".to_string(),
175
+ non_retryable: false,
176
+ details: None,
177
+ })
178
+ )
179
+ .is_none());
180
+ }
181
+
182
+ #[test]
183
+ fn no_non_retryable_application_failure() {
184
+ let rp = RetryPolicy {
185
+ initial_interval: Some(prost_dur!(from_secs(1))),
186
+ backoff_coefficient: 2.0,
187
+ maximum_interval: Some(prost_dur!(from_secs(10))),
188
+ maximum_attempts: 10,
189
+ non_retryable_error_types: vec![],
190
+ };
191
+ assert!(rp
192
+ .should_retry(
193
+ 1,
194
+ Some(&ApplicationFailureInfo {
195
+ r#type: "".to_string(),
196
+ non_retryable: true,
197
+ details: None,
198
+ })
199
+ )
200
+ .is_none());
201
+ }
202
+ }