temporalio 0.0.0 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (327) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +301 -0
  3. data/bridge/Cargo.lock +2888 -0
  4. data/bridge/Cargo.toml +27 -0
  5. data/bridge/sdk-core/ARCHITECTURE.md +76 -0
  6. data/bridge/sdk-core/Cargo.lock +2606 -0
  7. data/bridge/sdk-core/Cargo.toml +2 -0
  8. data/bridge/sdk-core/LICENSE.txt +23 -0
  9. data/bridge/sdk-core/README.md +104 -0
  10. data/bridge/sdk-core/arch_docs/diagrams/README.md +10 -0
  11. data/bridge/sdk-core/arch_docs/diagrams/sticky_queues.puml +40 -0
  12. data/bridge/sdk-core/arch_docs/diagrams/workflow_internals.svg +1 -0
  13. data/bridge/sdk-core/arch_docs/sticky_queues.md +51 -0
  14. data/bridge/sdk-core/client/Cargo.toml +40 -0
  15. data/bridge/sdk-core/client/LICENSE.txt +23 -0
  16. data/bridge/sdk-core/client/src/lib.rs +1286 -0
  17. data/bridge/sdk-core/client/src/metrics.rs +165 -0
  18. data/bridge/sdk-core/client/src/raw.rs +932 -0
  19. data/bridge/sdk-core/client/src/retry.rs +751 -0
  20. data/bridge/sdk-core/client/src/workflow_handle/mod.rs +185 -0
  21. data/bridge/sdk-core/core/Cargo.toml +116 -0
  22. data/bridge/sdk-core/core/LICENSE.txt +23 -0
  23. data/bridge/sdk-core/core/benches/workflow_replay.rs +76 -0
  24. data/bridge/sdk-core/core/src/abstractions.rs +166 -0
  25. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +1014 -0
  26. data/bridge/sdk-core/core/src/core_tests/child_workflows.rs +221 -0
  27. data/bridge/sdk-core/core/src/core_tests/determinism.rs +107 -0
  28. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +925 -0
  29. data/bridge/sdk-core/core/src/core_tests/mod.rs +100 -0
  30. data/bridge/sdk-core/core/src/core_tests/queries.rs +894 -0
  31. data/bridge/sdk-core/core/src/core_tests/replay_flag.rs +65 -0
  32. data/bridge/sdk-core/core/src/core_tests/workers.rs +259 -0
  33. data/bridge/sdk-core/core/src/core_tests/workflow_cancels.rs +124 -0
  34. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +2090 -0
  35. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +515 -0
  36. data/bridge/sdk-core/core/src/lib.rs +282 -0
  37. data/bridge/sdk-core/core/src/pollers/mod.rs +54 -0
  38. data/bridge/sdk-core/core/src/pollers/poll_buffer.rs +297 -0
  39. data/bridge/sdk-core/core/src/protosext/mod.rs +428 -0
  40. data/bridge/sdk-core/core/src/replay/mod.rs +215 -0
  41. data/bridge/sdk-core/core/src/retry_logic.rs +202 -0
  42. data/bridge/sdk-core/core/src/telemetry/log_export.rs +190 -0
  43. data/bridge/sdk-core/core/src/telemetry/metrics.rs +428 -0
  44. data/bridge/sdk-core/core/src/telemetry/mod.rs +407 -0
  45. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +78 -0
  46. data/bridge/sdk-core/core/src/test_help/mod.rs +889 -0
  47. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +580 -0
  48. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +1048 -0
  49. data/bridge/sdk-core/core/src/worker/activities.rs +481 -0
  50. data/bridge/sdk-core/core/src/worker/client/mocks.rs +87 -0
  51. data/bridge/sdk-core/core/src/worker/client.rs +373 -0
  52. data/bridge/sdk-core/core/src/worker/mod.rs +570 -0
  53. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +37 -0
  54. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +101 -0
  55. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +532 -0
  56. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +907 -0
  57. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +294 -0
  58. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +167 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +858 -0
  60. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +136 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +157 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +129 -0
  63. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +1450 -0
  64. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +316 -0
  65. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +178 -0
  66. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +708 -0
  67. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +439 -0
  68. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +435 -0
  69. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +175 -0
  70. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +242 -0
  71. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +96 -0
  72. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +1200 -0
  73. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +272 -0
  74. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +198 -0
  75. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +655 -0
  76. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +1200 -0
  77. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +145 -0
  78. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +88 -0
  79. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +985 -0
  80. data/bridge/sdk-core/core-api/Cargo.toml +32 -0
  81. data/bridge/sdk-core/core-api/LICENSE.txt +23 -0
  82. data/bridge/sdk-core/core-api/src/errors.rs +95 -0
  83. data/bridge/sdk-core/core-api/src/lib.rs +109 -0
  84. data/bridge/sdk-core/core-api/src/telemetry.rs +147 -0
  85. data/bridge/sdk-core/core-api/src/worker.rs +148 -0
  86. data/bridge/sdk-core/etc/deps.svg +162 -0
  87. data/bridge/sdk-core/etc/dynamic-config.yaml +2 -0
  88. data/bridge/sdk-core/etc/otel-collector-config.yaml +36 -0
  89. data/bridge/sdk-core/etc/prometheus.yaml +6 -0
  90. data/bridge/sdk-core/etc/regen-depgraph.sh +5 -0
  91. data/bridge/sdk-core/fsm/Cargo.toml +18 -0
  92. data/bridge/sdk-core/fsm/LICENSE.txt +23 -0
  93. data/bridge/sdk-core/fsm/README.md +3 -0
  94. data/bridge/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +27 -0
  95. data/bridge/sdk-core/fsm/rustfsm_procmacro/LICENSE.txt +23 -0
  96. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +647 -0
  97. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/progress.rs +8 -0
  98. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.rs +18 -0
  99. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +12 -0
  100. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dynamic_dest_pass.rs +41 -0
  101. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.rs +14 -0
  102. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/forgot_name_fail.stderr +11 -0
  103. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_arg_pass.rs +32 -0
  104. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/handler_pass.rs +31 -0
  105. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/medium_complex_pass.rs +46 -0
  106. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.rs +29 -0
  107. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +12 -0
  108. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/simple_pass.rs +32 -0
  109. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.rs +18 -0
  110. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/struct_event_variant_fail.stderr +5 -0
  111. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.rs +11 -0
  112. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_more_item_event_variant_fail.stderr +5 -0
  113. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.rs +11 -0
  114. data/bridge/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/tuple_zero_item_event_variant_fail.stderr +5 -0
  115. data/bridge/sdk-core/fsm/rustfsm_trait/Cargo.toml +14 -0
  116. data/bridge/sdk-core/fsm/rustfsm_trait/LICENSE.txt +23 -0
  117. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +249 -0
  118. data/bridge/sdk-core/fsm/src/lib.rs +2 -0
  119. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-23_history.bin +0 -0
  120. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-85_history.bin +0 -0
  121. data/bridge/sdk-core/histories/fail_wf_task.bin +0 -0
  122. data/bridge/sdk-core/histories/timer_workflow_history.bin +0 -0
  123. data/bridge/sdk-core/integ-with-otel.sh +7 -0
  124. data/bridge/sdk-core/protos/api_upstream/README.md +9 -0
  125. data/bridge/sdk-core/protos/api_upstream/api-linter.yaml +40 -0
  126. data/bridge/sdk-core/protos/api_upstream/buf.yaml +9 -0
  127. data/bridge/sdk-core/protos/api_upstream/build/go.mod +7 -0
  128. data/bridge/sdk-core/protos/api_upstream/build/go.sum +5 -0
  129. data/bridge/sdk-core/protos/api_upstream/build/tools.go +29 -0
  130. data/bridge/sdk-core/protos/api_upstream/dependencies/gogoproto/gogo.proto +141 -0
  131. data/bridge/sdk-core/protos/api_upstream/go.mod +6 -0
  132. data/bridge/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +89 -0
  133. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +260 -0
  134. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +112 -0
  135. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +47 -0
  136. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +57 -0
  137. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +56 -0
  138. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +170 -0
  139. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +118 -0
  140. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/interaction_type.proto +39 -0
  141. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +51 -0
  142. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +50 -0
  143. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +41 -0
  144. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +60 -0
  145. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +59 -0
  146. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +40 -0
  147. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +122 -0
  148. data/bridge/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +108 -0
  149. data/bridge/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +114 -0
  150. data/bridge/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +56 -0
  151. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +758 -0
  152. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +87 -0
  153. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +97 -0
  154. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +121 -0
  155. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +80 -0
  156. data/bridge/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +61 -0
  157. data/bridge/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +55 -0
  158. data/bridge/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +379 -0
  159. data/bridge/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +108 -0
  160. data/bridge/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +59 -0
  161. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +146 -0
  162. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +1168 -0
  163. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +415 -0
  164. data/bridge/sdk-core/protos/grpc/health/v1/health.proto +63 -0
  165. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  166. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +79 -0
  167. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +77 -0
  168. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +15 -0
  169. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +30 -0
  170. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  171. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +263 -0
  172. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +304 -0
  173. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +29 -0
  174. data/bridge/sdk-core/protos/testsrv_upstream/api-linter.yaml +38 -0
  175. data/bridge/sdk-core/protos/testsrv_upstream/buf.yaml +13 -0
  176. data/bridge/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +141 -0
  177. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +63 -0
  178. data/bridge/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +90 -0
  179. data/bridge/sdk-core/rustfmt.toml +1 -0
  180. data/bridge/sdk-core/sdk/Cargo.toml +47 -0
  181. data/bridge/sdk-core/sdk/LICENSE.txt +23 -0
  182. data/bridge/sdk-core/sdk/src/activity_context.rs +230 -0
  183. data/bridge/sdk-core/sdk/src/app_data.rs +37 -0
  184. data/bridge/sdk-core/sdk/src/interceptors.rs +50 -0
  185. data/bridge/sdk-core/sdk/src/lib.rs +794 -0
  186. data/bridge/sdk-core/sdk/src/payload_converter.rs +11 -0
  187. data/bridge/sdk-core/sdk/src/workflow_context/options.rs +295 -0
  188. data/bridge/sdk-core/sdk/src/workflow_context.rs +694 -0
  189. data/bridge/sdk-core/sdk/src/workflow_future.rs +499 -0
  190. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +30 -0
  191. data/bridge/sdk-core/sdk-core-protos/LICENSE.txt +23 -0
  192. data/bridge/sdk-core/sdk-core-protos/build.rs +107 -0
  193. data/bridge/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  194. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +544 -0
  195. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +230 -0
  196. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +1970 -0
  197. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  198. data/bridge/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  199. data/bridge/sdk-core/test-utils/Cargo.toml +36 -0
  200. data/bridge/sdk-core/test-utils/src/canned_histories.rs +1579 -0
  201. data/bridge/sdk-core/test-utils/src/histfetch.rs +28 -0
  202. data/bridge/sdk-core/test-utils/src/lib.rs +650 -0
  203. data/bridge/sdk-core/tests/integ_tests/client_tests.rs +36 -0
  204. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +128 -0
  205. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +221 -0
  206. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +37 -0
  207. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +133 -0
  208. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +437 -0
  209. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +93 -0
  210. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +878 -0
  211. data/bridge/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +61 -0
  212. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +59 -0
  213. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +58 -0
  214. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +50 -0
  215. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +60 -0
  216. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +54 -0
  217. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +788 -0
  218. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +53 -0
  219. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +113 -0
  220. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +223 -0
  221. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +93 -0
  222. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +167 -0
  223. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +99 -0
  224. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +131 -0
  225. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +75 -0
  226. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +597 -0
  227. data/bridge/sdk-core/tests/load_tests.rs +191 -0
  228. data/bridge/sdk-core/tests/main.rs +113 -0
  229. data/bridge/sdk-core/tests/runner.rs +93 -0
  230. data/bridge/src/connection.rs +186 -0
  231. data/bridge/src/lib.rs +239 -0
  232. data/bridge/src/runtime.rs +54 -0
  233. data/bridge/src/worker.rs +124 -0
  234. data/ext/Rakefile +9 -0
  235. data/lib/bridge.so +0 -0
  236. data/lib/gen/dependencies/gogoproto/gogo_pb.rb +14 -0
  237. data/lib/gen/temporal/api/batch/v1/message_pb.rb +50 -0
  238. data/lib/gen/temporal/api/command/v1/message_pb.rb +174 -0
  239. data/lib/gen/temporal/api/common/v1/message_pb.rb +69 -0
  240. data/lib/gen/temporal/api/enums/v1/batch_operation_pb.rb +33 -0
  241. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +39 -0
  242. data/lib/gen/temporal/api/enums/v1/common_pb.rb +42 -0
  243. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +68 -0
  244. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +77 -0
  245. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +25 -0
  246. data/lib/gen/temporal/api/enums/v1/namespace_pb.rb +37 -0
  247. data/lib/gen/temporal/api/enums/v1/query_pb.rb +31 -0
  248. data/lib/gen/temporal/api/enums/v1/reset_pb.rb +24 -0
  249. data/lib/gen/temporal/api/enums/v1/schedule_pb.rb +28 -0
  250. data/lib/gen/temporal/api/enums/v1/task_queue_pb.rb +30 -0
  251. data/lib/gen/temporal/api/enums/v1/update_pb.rb +23 -0
  252. data/lib/gen/temporal/api/enums/v1/workflow_pb.rb +89 -0
  253. data/lib/gen/temporal/api/errordetails/v1/message_pb.rb +84 -0
  254. data/lib/gen/temporal/api/failure/v1/message_pb.rb +83 -0
  255. data/lib/gen/temporal/api/filter/v1/message_pb.rb +40 -0
  256. data/lib/gen/temporal/api/history/v1/message_pb.rb +490 -0
  257. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +49 -0
  258. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +63 -0
  259. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +85 -0
  260. data/lib/gen/temporal/api/operatorservice/v1/service_pb.rb +20 -0
  261. data/lib/gen/temporal/api/query/v1/message_pb.rb +38 -0
  262. data/lib/gen/temporal/api/replication/v1/message_pb.rb +37 -0
  263. data/lib/gen/temporal/api/schedule/v1/message_pb.rb +149 -0
  264. data/lib/gen/temporal/api/taskqueue/v1/message_pb.rb +73 -0
  265. data/lib/gen/temporal/api/version/v1/message_pb.rb +41 -0
  266. data/lib/gen/temporal/api/workflow/v1/message_pb.rb +111 -0
  267. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +788 -0
  268. data/lib/gen/temporal/api/workflowservice/v1/service_pb.rb +20 -0
  269. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +58 -0
  270. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +57 -0
  271. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +222 -0
  272. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +57 -0
  273. data/lib/gen/temporal/sdk/core/common/common_pb.rb +22 -0
  274. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +34 -0
  275. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +27 -0
  276. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +165 -0
  277. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +196 -0
  278. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +34 -0
  279. data/lib/temporalio/activity/context.rb +97 -0
  280. data/lib/temporalio/activity/info.rb +67 -0
  281. data/lib/temporalio/activity.rb +85 -0
  282. data/lib/temporalio/bridge/error.rb +8 -0
  283. data/lib/temporalio/bridge.rb +14 -0
  284. data/lib/temporalio/client/implementation.rb +340 -0
  285. data/lib/temporalio/client/workflow_handle.rb +243 -0
  286. data/lib/temporalio/client.rb +131 -0
  287. data/lib/temporalio/connection.rb +751 -0
  288. data/lib/temporalio/data_converter.rb +191 -0
  289. data/lib/temporalio/error/failure.rb +194 -0
  290. data/lib/temporalio/error/workflow_failure.rb +19 -0
  291. data/lib/temporalio/errors.rb +40 -0
  292. data/lib/temporalio/failure_converter/base.rb +26 -0
  293. data/lib/temporalio/failure_converter/basic.rb +319 -0
  294. data/lib/temporalio/failure_converter.rb +7 -0
  295. data/lib/temporalio/interceptor/chain.rb +28 -0
  296. data/lib/temporalio/interceptor/client.rb +123 -0
  297. data/lib/temporalio/payload_codec/base.rb +32 -0
  298. data/lib/temporalio/payload_converter/base.rb +24 -0
  299. data/lib/temporalio/payload_converter/bytes.rb +27 -0
  300. data/lib/temporalio/payload_converter/composite.rb +49 -0
  301. data/lib/temporalio/payload_converter/encoding_base.rb +35 -0
  302. data/lib/temporalio/payload_converter/json.rb +26 -0
  303. data/lib/temporalio/payload_converter/nil.rb +26 -0
  304. data/lib/temporalio/payload_converter.rb +14 -0
  305. data/lib/temporalio/retry_policy.rb +82 -0
  306. data/lib/temporalio/retry_state.rb +35 -0
  307. data/lib/temporalio/runtime.rb +25 -0
  308. data/lib/temporalio/timeout_type.rb +29 -0
  309. data/lib/temporalio/version.rb +3 -0
  310. data/lib/temporalio/worker/activity_runner.rb +92 -0
  311. data/lib/temporalio/worker/activity_worker.rb +138 -0
  312. data/lib/temporalio/worker/reactor.rb +46 -0
  313. data/lib/temporalio/worker/runner.rb +63 -0
  314. data/lib/temporalio/worker/sync_worker.rb +88 -0
  315. data/lib/temporalio/worker/thread_pool_executor.rb +51 -0
  316. data/lib/temporalio/worker.rb +198 -0
  317. data/lib/temporalio/workflow/execution_info.rb +54 -0
  318. data/lib/temporalio/workflow/execution_status.rb +36 -0
  319. data/lib/temporalio/workflow/id_reuse_policy.rb +36 -0
  320. data/lib/temporalio/workflow/query_reject_condition.rb +33 -0
  321. data/lib/temporalio.rb +12 -1
  322. data/lib/thermite_patch.rb +23 -0
  323. data/temporalio.gemspec +45 -0
  324. metadata +566 -9
  325. data/lib/temporal/version.rb +0 -3
  326. data/lib/temporal.rb +0 -4
  327. data/temporal.gemspec +0 -20
@@ -0,0 +1,2090 @@
1
+ use crate::{
2
+ advance_fut, job_assert,
3
+ replay::TestHistoryBuilder,
4
+ test_help::{
5
+ build_fake_worker, build_mock_pollers, build_multihist_mock_sg, canned_histories,
6
+ gen_assert_and_fail, gen_assert_and_reply, hist_to_poll_resp, mock_sdk, mock_sdk_cfg,
7
+ mock_worker, poll_and_reply, poll_and_reply_clears_outstanding_evicts, single_hist_mock_sg,
8
+ test_worker_cfg, FakeWfResponses, MockPollCfg, MocksHolder, ResponseType,
9
+ WorkflowCachingPolicy::{self, AfterEveryReply, NonSticky},
10
+ },
11
+ worker::client::mocks::{mock_manual_workflow_client, mock_workflow_client},
12
+ Worker,
13
+ };
14
+ use futures::{stream, FutureExt};
15
+ use rstest::{fixture, rstest};
16
+ use std::{
17
+ collections::{HashMap, VecDeque},
18
+ sync::{
19
+ atomic::{AtomicU64, Ordering},
20
+ Arc,
21
+ },
22
+ time::Duration,
23
+ };
24
+ use temporal_sdk::{ActivityOptions, CancellableFuture, WfContext};
25
+ use temporal_sdk_core_api::{errors::PollWfError, Worker as WorkerTrait};
26
+ use temporal_sdk_core_protos::{
27
+ coresdk::{
28
+ activity_result::{self as ar, activity_resolution, ActivityResolution},
29
+ workflow_activation::{
30
+ remove_from_cache::EvictionReason, workflow_activation_job, FireTimer, ResolveActivity,
31
+ StartWorkflow, UpdateRandomSeed, WorkflowActivationJob,
32
+ },
33
+ workflow_commands::{
34
+ ActivityCancellationType, CancelTimer, CompleteWorkflowExecution,
35
+ ContinueAsNewWorkflowExecution, FailWorkflowExecution, RequestCancelActivity,
36
+ ScheduleActivity,
37
+ },
38
+ workflow_completion::WorkflowActivationCompletion,
39
+ },
40
+ default_wes_attribs,
41
+ temporal::api::{
42
+ command::v1::command::Attributes,
43
+ common::v1::{Payload, RetryPolicy},
44
+ enums::v1::{EventType, WorkflowTaskFailedCause},
45
+ failure::v1::Failure,
46
+ history::v1::{history_event, TimerFiredEventAttributes},
47
+ workflowservice::v1::{
48
+ GetWorkflowExecutionHistoryResponse, RespondWorkflowTaskCompletedResponse,
49
+ },
50
+ },
51
+ DEFAULT_WORKFLOW_TYPE,
52
+ };
53
+ use temporal_sdk_core_test_utils::{fanout_tasks, start_timer_cmd};
54
+ use tokio::{
55
+ join,
56
+ sync::{Barrier, Semaphore},
57
+ };
58
+
59
+ #[fixture(hist_batches = &[])]
60
+ fn single_timer_setup(hist_batches: &'static [usize]) -> Worker {
61
+ let wfid = "fake_wf_id";
62
+
63
+ let t = canned_histories::single_timer("1");
64
+ build_fake_worker(wfid, t, hist_batches)
65
+ }
66
+
67
+ #[fixture(hist_batches = &[])]
68
+ fn single_activity_setup(hist_batches: &'static [usize]) -> Worker {
69
+ let wfid = "fake_wf_id";
70
+
71
+ let t = canned_histories::single_activity("fake_activity");
72
+ build_fake_worker(wfid, t, hist_batches)
73
+ }
74
+
75
+ #[fixture(hist_batches = &[])]
76
+ fn single_activity_failure_setup(hist_batches: &'static [usize]) -> Worker {
77
+ let wfid = "fake_wf_id";
78
+
79
+ let t = canned_histories::single_failed_activity("fake_activity");
80
+ build_fake_worker(wfid, t, hist_batches)
81
+ }
82
+
83
+ #[rstest]
84
+ #[case::incremental(single_timer_setup(&[1, 2]), NonSticky)]
85
+ #[case::replay(single_timer_setup(&[2]), NonSticky)]
86
+ #[case::incremental_evict(single_timer_setup(&[1, 2]), AfterEveryReply)]
87
+ #[case::replay_evict(single_timer_setup(&[2]), AfterEveryReply)]
88
+ #[tokio::test]
89
+ async fn single_timer(#[case] worker: Worker, #[case] evict: WorkflowCachingPolicy) {
90
+ poll_and_reply(
91
+ &worker,
92
+ evict,
93
+ &[
94
+ gen_assert_and_reply(
95
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
96
+ vec![start_timer_cmd(1, Duration::from_secs(1))],
97
+ ),
98
+ gen_assert_and_reply(
99
+ &job_assert!(workflow_activation_job::Variant::FireTimer(_)),
100
+ vec![CompleteWorkflowExecution { result: None }.into()],
101
+ ),
102
+ ],
103
+ )
104
+ .await;
105
+ }
106
+
107
+ #[rstest(worker,
108
+ case::incremental(single_activity_setup(&[1, 2])),
109
+ case::incremental_activity_failure(single_activity_failure_setup(&[1, 2])),
110
+ case::replay(single_activity_setup(&[2])),
111
+ case::replay_activity_failure(single_activity_failure_setup(&[2]))
112
+ )]
113
+ #[tokio::test]
114
+ async fn single_activity_completion(worker: Worker) {
115
+ poll_and_reply(
116
+ &worker,
117
+ NonSticky,
118
+ &[
119
+ gen_assert_and_reply(
120
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
121
+ vec![ScheduleActivity {
122
+ activity_id: "fake_activity".to_string(),
123
+ ..Default::default()
124
+ }
125
+ .into()],
126
+ ),
127
+ gen_assert_and_reply(
128
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(_)),
129
+ vec![CompleteWorkflowExecution { result: None }.into()],
130
+ ),
131
+ ],
132
+ )
133
+ .await;
134
+ }
135
+
136
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
137
+ #[tokio::test]
138
+ async fn parallel_timer_test_across_wf_bridge(hist_batches: &'static [usize]) {
139
+ let wfid = "fake_wf_id";
140
+ let timer_1_id = 1;
141
+ let timer_2_id = 2;
142
+
143
+ let t = canned_histories::parallel_timer(
144
+ timer_1_id.to_string().as_str(),
145
+ timer_2_id.to_string().as_str(),
146
+ );
147
+ let core = build_fake_worker(wfid, t, hist_batches);
148
+
149
+ poll_and_reply(
150
+ &core,
151
+ NonSticky,
152
+ &[
153
+ gen_assert_and_reply(
154
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
155
+ vec![
156
+ start_timer_cmd(timer_1_id, Duration::from_secs(1)),
157
+ start_timer_cmd(timer_2_id, Duration::from_secs(1)),
158
+ ],
159
+ ),
160
+ gen_assert_and_reply(
161
+ &|res| {
162
+ assert_matches!(
163
+ res.jobs.as_slice(),
164
+ [
165
+ WorkflowActivationJob {
166
+ variant: Some(workflow_activation_job::Variant::FireTimer(
167
+ FireTimer { seq: t1_id }
168
+ )),
169
+ },
170
+ WorkflowActivationJob {
171
+ variant: Some(workflow_activation_job::Variant::FireTimer(
172
+ FireTimer { seq: t2_id }
173
+ )),
174
+ }
175
+ ] => {
176
+ assert_eq!(t1_id, &timer_1_id);
177
+ assert_eq!(t2_id, &timer_2_id);
178
+ }
179
+ );
180
+ },
181
+ vec![CompleteWorkflowExecution { result: None }.into()],
182
+ ),
183
+ ],
184
+ )
185
+ .await;
186
+ }
187
+
188
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
189
+ #[tokio::test]
190
+ async fn timer_cancel(hist_batches: &'static [usize]) {
191
+ let wfid = "fake_wf_id";
192
+ let timer_id = 1;
193
+ let cancel_timer_id = 2;
194
+
195
+ let t = canned_histories::cancel_timer(
196
+ timer_id.to_string().as_str(),
197
+ cancel_timer_id.to_string().as_str(),
198
+ );
199
+ let core = build_fake_worker(wfid, t, hist_batches);
200
+
201
+ poll_and_reply(
202
+ &core,
203
+ NonSticky,
204
+ &[
205
+ gen_assert_and_reply(
206
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
207
+ vec![
208
+ start_timer_cmd(cancel_timer_id, Duration::from_secs(1)),
209
+ start_timer_cmd(timer_id, Duration::from_secs(1)),
210
+ ],
211
+ ),
212
+ gen_assert_and_reply(
213
+ &job_assert!(workflow_activation_job::Variant::FireTimer(_)),
214
+ vec![
215
+ CancelTimer {
216
+ seq: cancel_timer_id,
217
+ }
218
+ .into(),
219
+ CompleteWorkflowExecution { result: None }.into(),
220
+ ],
221
+ ),
222
+ ],
223
+ )
224
+ .await;
225
+ }
226
+
227
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
228
+ #[tokio::test]
229
+ async fn scheduled_activity_cancellation_try_cancel(hist_batches: &'static [usize]) {
230
+ let wfid = "fake_wf_id";
231
+ let activity_seq = 1;
232
+ let activity_id = "fake_activity";
233
+ let signal_id = "signal";
234
+
235
+ let t = canned_histories::cancel_scheduled_activity(activity_id, signal_id);
236
+ let core = build_fake_worker(wfid, t, hist_batches);
237
+
238
+ poll_and_reply(
239
+ &core,
240
+ NonSticky,
241
+ &[
242
+ gen_assert_and_reply(
243
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
244
+ vec![ScheduleActivity {
245
+ seq: activity_seq,
246
+ activity_id: activity_id.to_string(),
247
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
248
+ ..Default::default()
249
+ }
250
+ .into()],
251
+ ),
252
+ gen_assert_and_reply(
253
+ &job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
254
+ vec![RequestCancelActivity { seq: activity_seq }.into()],
255
+ ),
256
+ // Activity is getting resolved right away as we are in the TryCancel mode.
257
+ gen_assert_and_reply(
258
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(_)),
259
+ vec![CompleteWorkflowExecution { result: None }.into()],
260
+ ),
261
+ ],
262
+ )
263
+ .await;
264
+ }
265
+
266
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
267
+ #[tokio::test]
268
+ async fn scheduled_activity_timeout(hist_batches: &'static [usize]) {
269
+ let wfid = "fake_wf_id";
270
+ let activity_seq = 1;
271
+ let activity_id = "fake_activity";
272
+
273
+ let t = canned_histories::scheduled_activity_timeout(activity_id);
274
+ let core = build_fake_worker(wfid, t, hist_batches);
275
+ poll_and_reply(
276
+ &core,
277
+ NonSticky,
278
+ &[
279
+ gen_assert_and_reply(
280
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
281
+ vec![ScheduleActivity {
282
+ seq: activity_seq,
283
+ activity_id: activity_id.to_string(),
284
+ ..Default::default()
285
+ }
286
+ .into()],
287
+ ),
288
+ // Activity is getting resolved right away as it has been timed out.
289
+ gen_assert_and_reply(
290
+ &|res| {
291
+ assert_matches!(
292
+ res.jobs.as_slice(),
293
+ [
294
+ WorkflowActivationJob {
295
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(
296
+ ResolveActivity {
297
+ seq,
298
+ result: Some(ActivityResolution {
299
+ status: Some(activity_resolution::Status::Failed(ar::Failure {
300
+ failure: Some(failure)
301
+ })),
302
+ })
303
+ }
304
+ )),
305
+ }
306
+ ] => {
307
+ assert_eq!(failure.message, "Activity task timed out".to_string());
308
+ assert_eq!(*seq, activity_seq);
309
+ }
310
+ );
311
+ },
312
+ vec![CompleteWorkflowExecution { result: None }.into()],
313
+ ),
314
+ ],
315
+ )
316
+ .await;
317
+ }
318
+
319
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
320
+ #[tokio::test]
321
+ async fn started_activity_timeout(hist_batches: &'static [usize]) {
322
+ let wfid = "fake_wf_id";
323
+ let activity_seq = 1;
324
+
325
+ let t = canned_histories::started_activity_timeout(activity_seq.to_string().as_str());
326
+ let core = build_fake_worker(wfid, t, hist_batches);
327
+
328
+ poll_and_reply(
329
+ &core,
330
+ NonSticky,
331
+ &[
332
+ gen_assert_and_reply(
333
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
334
+ vec![ScheduleActivity {
335
+ seq: activity_seq,
336
+ activity_id: activity_seq.to_string(),
337
+ ..Default::default()
338
+ }
339
+ .into()],
340
+ ),
341
+ // Activity is getting resolved right away as it has been timed out.
342
+ gen_assert_and_reply(
343
+ &|res| {
344
+ assert_matches!(
345
+ res.jobs.as_slice(),
346
+ [
347
+ WorkflowActivationJob {
348
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(
349
+ ResolveActivity {
350
+ seq,
351
+ result: Some(ActivityResolution {
352
+ status: Some(activity_resolution::Status::Failed(ar::Failure {
353
+ failure: Some(failure)
354
+ })),
355
+ })
356
+ }
357
+ )),
358
+ }
359
+ ] => {
360
+ assert_eq!(failure.message, "Activity task timed out".to_string());
361
+ assert_eq!(*seq, activity_seq);
362
+ }
363
+ );
364
+ },
365
+ vec![CompleteWorkflowExecution { result: None }.into()],
366
+ ),
367
+ ],
368
+ )
369
+ .await;
370
+ }
371
+
372
+ #[rstest(hist_batches, case::incremental(&[1, 3]), case::replay(&[3]))]
373
+ #[tokio::test]
374
+ async fn cancelled_activity_timeout(hist_batches: &'static [usize]) {
375
+ let wfid = "fake_wf_id";
376
+ let activity_seq = 0;
377
+ let activity_id = "fake_activity";
378
+ let signal_id = "signal";
379
+
380
+ let t = canned_histories::scheduled_cancelled_activity_timeout(activity_id, signal_id);
381
+ let core = build_fake_worker(wfid, t, hist_batches);
382
+
383
+ poll_and_reply(
384
+ &core,
385
+ NonSticky,
386
+ &[
387
+ gen_assert_and_reply(
388
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
389
+ vec![ScheduleActivity {
390
+ seq: activity_seq,
391
+ activity_id: activity_id.to_string(),
392
+ ..Default::default()
393
+ }
394
+ .into()],
395
+ ),
396
+ gen_assert_and_reply(
397
+ &job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
398
+ vec![RequestCancelActivity { seq: activity_seq }.into()],
399
+ ),
400
+ // Activity is resolved right away as it has timed out.
401
+ gen_assert_and_reply(
402
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(
403
+ ResolveActivity {
404
+ seq: _,
405
+ result: Some(ActivityResolution {
406
+ status: Some(activity_resolution::Status::Cancelled(..)),
407
+ })
408
+ }
409
+ )),
410
+ vec![CompleteWorkflowExecution { result: None }.into()],
411
+ ),
412
+ ],
413
+ )
414
+ .await;
415
+ }
416
+
417
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
418
+ #[tokio::test]
419
+ async fn scheduled_activity_cancellation_abandon(hist_batches: &'static [usize]) {
420
+ let wfid = "fake_wf_id";
421
+ let activity_id = 1;
422
+ let signal_id = "signal";
423
+
424
+ let t = canned_histories::cancel_scheduled_activity_abandon(
425
+ activity_id.to_string().as_str(),
426
+ signal_id,
427
+ );
428
+ let core = build_fake_worker(wfid, t, hist_batches);
429
+
430
+ verify_activity_cancellation(&core, activity_id, ActivityCancellationType::Abandon).await;
431
+ }
432
+
433
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
434
+ #[tokio::test]
435
+ async fn started_activity_cancellation_abandon(hist_batches: &'static [usize]) {
436
+ let wfid = "fake_wf_id";
437
+ let activity_id = 1;
438
+ let signal_id = "signal";
439
+
440
+ let t = canned_histories::cancel_started_activity_abandon(
441
+ activity_id.to_string().as_str(),
442
+ signal_id,
443
+ );
444
+ let core = build_fake_worker(wfid, t, hist_batches);
445
+
446
+ verify_activity_cancellation(&core, activity_id, ActivityCancellationType::Abandon).await;
447
+ }
448
+
449
+ #[rstest(hist_batches, case::incremental(&[1, 2, 3, 4]), case::replay(&[4]))]
450
+ #[tokio::test]
451
+ async fn abandoned_activities_ignore_start_and_complete(hist_batches: &'static [usize]) {
452
+ let wfid = "fake_wf_id";
453
+ let wf_type = DEFAULT_WORKFLOW_TYPE;
454
+ let activity_id = "1";
455
+
456
+ let mut t = TestHistoryBuilder::default();
457
+ t.add_by_type(EventType::WorkflowExecutionStarted);
458
+ t.add_full_wf_task();
459
+ let act_scheduled_event_id = t.add_activity_task_scheduled(activity_id);
460
+ let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
461
+ t.add_timer_fired(timer_started_event_id, "1".to_string());
462
+ t.add_full_wf_task();
463
+ let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
464
+ let act_started_event_id = t.add_activity_task_started(act_scheduled_event_id);
465
+ t.add_activity_task_completed(
466
+ act_scheduled_event_id,
467
+ act_started_event_id,
468
+ Default::default(),
469
+ );
470
+ t.add_full_wf_task();
471
+ t.add_timer_fired(timer_started_event_id, "2".to_string());
472
+ t.add_full_wf_task();
473
+ t.add_workflow_execution_completed();
474
+ let mock = mock_workflow_client();
475
+ let mut worker = mock_sdk(MockPollCfg::from_resp_batches(wfid, t, hist_batches, mock));
476
+
477
+ worker.register_wf(wf_type.to_owned(), |ctx: WfContext| async move {
478
+ let act_fut = ctx.activity(ActivityOptions {
479
+ activity_type: "echo_activity".to_string(),
480
+ start_to_close_timeout: Some(Duration::from_secs(5)),
481
+ cancellation_type: ActivityCancellationType::Abandon,
482
+ ..Default::default()
483
+ });
484
+ ctx.timer(Duration::from_secs(1)).await;
485
+ act_fut.cancel(&ctx);
486
+ ctx.timer(Duration::from_secs(3)).await;
487
+ act_fut.await;
488
+ Ok(().into())
489
+ });
490
+ worker
491
+ .submit_wf(wfid, wf_type, vec![], Default::default())
492
+ .await
493
+ .unwrap();
494
+ worker.run_until_done().await.unwrap();
495
+ }
496
+
497
+ #[rstest(hist_batches, case::incremental(&[1, 3]), case::replay(&[3]))]
498
+ #[tokio::test]
499
+ async fn scheduled_activity_cancellation_try_cancel_task_canceled(hist_batches: &'static [usize]) {
500
+ let wfid = "fake_wf_id";
501
+ let activity_id = 1;
502
+ let signal_id = "signal";
503
+
504
+ let t = canned_histories::cancel_scheduled_activity_with_activity_task_cancel(
505
+ activity_id.to_string().as_str(),
506
+ signal_id,
507
+ );
508
+ let core = build_fake_worker(wfid, t, hist_batches);
509
+
510
+ verify_activity_cancellation(&core, activity_id, ActivityCancellationType::TryCancel).await;
511
+ }
512
+
513
+ #[rstest(hist_batches, case::incremental(&[1, 3]), case::replay(&[3]))]
514
+ #[tokio::test]
515
+ async fn started_activity_cancellation_try_cancel_task_canceled(hist_batches: &'static [usize]) {
516
+ let wfid = "fake_wf_id";
517
+ let activity_id = 1;
518
+ let signal_id = "signal";
519
+
520
+ let t = canned_histories::cancel_started_activity_with_activity_task_cancel(
521
+ activity_id.to_string().as_str(),
522
+ signal_id,
523
+ );
524
+ let core = build_fake_worker(wfid, t, hist_batches);
525
+
526
+ verify_activity_cancellation(&core, activity_id, ActivityCancellationType::TryCancel).await;
527
+ }
528
+
529
+ /// Verification for try cancel & abandon histories
530
+ async fn verify_activity_cancellation(
531
+ worker: &Worker,
532
+ activity_seq: u32,
533
+ cancel_type: ActivityCancellationType,
534
+ ) {
535
+ poll_and_reply(
536
+ worker,
537
+ NonSticky,
538
+ &[
539
+ gen_assert_and_reply(
540
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
541
+ vec![ScheduleActivity {
542
+ seq: activity_seq,
543
+ activity_id: activity_seq.to_string(),
544
+ cancellation_type: cancel_type as i32,
545
+ ..Default::default()
546
+ }
547
+ .into()],
548
+ ),
549
+ gen_assert_and_reply(
550
+ &job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
551
+ vec![RequestCancelActivity { seq: activity_seq }.into()],
552
+ ),
553
+ // Activity should be resolved right away
554
+ gen_assert_and_reply(
555
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(
556
+ ResolveActivity {
557
+ seq: _,
558
+ result: Some(ActivityResolution {
559
+ status: Some(activity_resolution::Status::Cancelled(..)),
560
+ })
561
+ }
562
+ )),
563
+ vec![CompleteWorkflowExecution { result: None }.into()],
564
+ ),
565
+ ],
566
+ )
567
+ .await;
568
+ }
569
+
570
+ #[rstest(hist_batches, case::incremental(&[1, 2, 3, 4]), case::replay(&[4]))]
571
+ #[tokio::test]
572
+ async fn scheduled_activity_cancellation_wait_for_cancellation(hist_batches: &'static [usize]) {
573
+ let wfid = "fake_wf_id";
574
+ let activity_id = 1;
575
+ let signal_id = "signal";
576
+
577
+ let t = canned_histories::cancel_scheduled_activity_with_signal_and_activity_task_cancel(
578
+ activity_id.to_string().as_str(),
579
+ signal_id,
580
+ );
581
+ let core = build_fake_worker(wfid, t, hist_batches);
582
+
583
+ verify_activity_cancellation_wait_for_cancellation(activity_id, &core).await;
584
+ }
585
+
586
+ #[rstest(hist_batches, case::incremental(&[1, 2, 3, 4]), case::replay(&[4]))]
587
+ #[tokio::test]
588
+ async fn started_activity_cancellation_wait_for_cancellation(hist_batches: &'static [usize]) {
589
+ let wfid = "fake_wf_id";
590
+ let activity_id = 1;
591
+ let signal_id = "signal";
592
+
593
+ let t = canned_histories::cancel_started_activity_with_signal_and_activity_task_cancel(
594
+ activity_id.to_string().as_str(),
595
+ signal_id,
596
+ );
597
+ let core = build_fake_worker(wfid, t, hist_batches);
598
+
599
+ verify_activity_cancellation_wait_for_cancellation(activity_id, &core).await;
600
+ }
601
+
602
+ async fn verify_activity_cancellation_wait_for_cancellation(activity_id: u32, worker: &Worker) {
603
+ poll_and_reply(
604
+ worker,
605
+ NonSticky,
606
+ &[
607
+ gen_assert_and_reply(
608
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
609
+ vec![ScheduleActivity {
610
+ seq: activity_id,
611
+ activity_id: activity_id.to_string(),
612
+ cancellation_type: ActivityCancellationType::WaitCancellationCompleted as i32,
613
+ ..Default::default()
614
+ }
615
+ .into()],
616
+ ),
617
+ gen_assert_and_reply(
618
+ &job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
619
+ vec![RequestCancelActivity { seq: activity_id }.into()],
620
+ ),
621
+ // Making sure that activity is not resolved until it's cancelled.
622
+ gen_assert_and_reply(
623
+ &job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
624
+ vec![],
625
+ ),
626
+ // Now ActivityTaskCanceled has been processed and activity can be resolved.
627
+ gen_assert_and_reply(
628
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(
629
+ ResolveActivity {
630
+ seq: _,
631
+ result: Some(ActivityResolution {
632
+ status: Some(activity_resolution::Status::Cancelled(..)),
633
+ })
634
+ }
635
+ )),
636
+ vec![CompleteWorkflowExecution { result: None }.into()],
637
+ ),
638
+ ],
639
+ )
640
+ .await;
641
+ }
642
+
643
+ #[tokio::test]
644
+ async fn workflow_update_random_seed_on_workflow_reset() {
645
+ let wfid = "fake_wf_id";
646
+ let new_run_id = "86E39A5F-AE31-4626-BDFE-398EE072D156";
647
+ let timer_1_id = 1;
648
+ let randomness_seed_from_start = AtomicU64::new(0);
649
+
650
+ let t = canned_histories::workflow_fails_with_reset_after_timer(
651
+ timer_1_id.to_string().as_str(),
652
+ new_run_id,
653
+ );
654
+ let core = build_fake_worker(wfid, t, [2]);
655
+
656
+ poll_and_reply(
657
+ &core,
658
+ NonSticky,
659
+ &[
660
+ gen_assert_and_reply(
661
+ &|res| {
662
+ assert_matches!(
663
+ res.jobs.as_slice(),
664
+ [WorkflowActivationJob {
665
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(
666
+ StartWorkflow{randomness_seed, ..}
667
+ )),
668
+ }] => {
669
+ randomness_seed_from_start.store(*randomness_seed, Ordering::SeqCst);
670
+ }
671
+ );
672
+ },
673
+ vec![start_timer_cmd(timer_1_id, Duration::from_secs(1))],
674
+ ),
675
+ gen_assert_and_reply(
676
+ &|res| {
677
+ assert_matches!(
678
+ res.jobs.as_slice(),
679
+ [WorkflowActivationJob {
680
+ variant: Some(workflow_activation_job::Variant::FireTimer(_),),
681
+ },
682
+ WorkflowActivationJob {
683
+ variant: Some(workflow_activation_job::Variant::UpdateRandomSeed(
684
+ UpdateRandomSeed{randomness_seed})),
685
+ }] => {
686
+ assert_ne!(randomness_seed_from_start.load(Ordering::SeqCst),
687
+ *randomness_seed);
688
+ }
689
+ );
690
+ },
691
+ vec![CompleteWorkflowExecution { result: None }.into()],
692
+ ),
693
+ ],
694
+ )
695
+ .await;
696
+ }
697
+
698
+ #[tokio::test]
699
+ async fn cancel_timer_before_sent_wf_bridge() {
700
+ let wfid = "fake_wf_id";
701
+ let cancel_timer_id = 1;
702
+
703
+ let mut t = TestHistoryBuilder::default();
704
+ t.add_by_type(EventType::WorkflowExecutionStarted);
705
+ t.add_full_wf_task();
706
+ t.add_workflow_execution_completed();
707
+
708
+ let core = build_fake_worker(wfid, t, [1]);
709
+
710
+ poll_and_reply(
711
+ &core,
712
+ NonSticky,
713
+ &[gen_assert_and_reply(
714
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
715
+ vec![
716
+ start_timer_cmd(cancel_timer_id, Duration::from_secs(1)),
717
+ CancelTimer {
718
+ seq: cancel_timer_id,
719
+ }
720
+ .into(),
721
+ CompleteWorkflowExecution { result: None }.into(),
722
+ ],
723
+ )],
724
+ )
725
+ .await;
726
+ }
727
+
728
+ #[rstest]
729
+ #[case::no_evict_inc(&[1, 2, 2], NonSticky)]
730
+ #[case::no_evict(&[2, 2], NonSticky)]
731
+ #[tokio::test]
732
+ async fn complete_activation_with_failure(
733
+ #[case] batches: &'static [usize],
734
+ #[case] evict: WorkflowCachingPolicy,
735
+ ) {
736
+ let wfid = "fake_wf_id";
737
+ let timer_id = 1;
738
+
739
+ let hist =
740
+ canned_histories::workflow_fails_with_failure_after_timer(timer_id.to_string().as_str());
741
+ let mock_sg = build_multihist_mock_sg(
742
+ vec![FakeWfResponses {
743
+ wf_id: wfid.to_string(),
744
+ hist,
745
+ response_batches: batches.iter().map(Into::into).collect(),
746
+ }],
747
+ true,
748
+ 1,
749
+ );
750
+ let core = mock_worker(mock_sg);
751
+
752
+ poll_and_reply(
753
+ &core,
754
+ evict,
755
+ &[
756
+ gen_assert_and_reply(
757
+ &|_| {},
758
+ vec![start_timer_cmd(timer_id, Duration::from_secs(1))],
759
+ ),
760
+ gen_assert_and_fail(&|_| {}),
761
+ gen_assert_and_reply(
762
+ &job_assert!(workflow_activation_job::Variant::FireTimer(_)),
763
+ vec![CompleteWorkflowExecution { result: None }.into()],
764
+ ),
765
+ ],
766
+ )
767
+ .await;
768
+ core.shutdown().await;
769
+ }
770
+
771
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
772
+ #[tokio::test]
773
+ async fn simple_timer_fail_wf_execution(hist_batches: &'static [usize]) {
774
+ let wfid = "fake_wf_id";
775
+ let timer_id = 1;
776
+
777
+ let t = canned_histories::single_timer(timer_id.to_string().as_str());
778
+ let core = build_fake_worker(wfid, t, hist_batches);
779
+
780
+ poll_and_reply(
781
+ &core,
782
+ NonSticky,
783
+ &[
784
+ gen_assert_and_reply(
785
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
786
+ vec![start_timer_cmd(timer_id, Duration::from_secs(1))],
787
+ ),
788
+ gen_assert_and_reply(
789
+ &job_assert!(workflow_activation_job::Variant::FireTimer(_)),
790
+ vec![FailWorkflowExecution {
791
+ failure: Some(Failure {
792
+ message: "I'm ded".to_string(),
793
+ ..Default::default()
794
+ }),
795
+ }
796
+ .into()],
797
+ ),
798
+ ],
799
+ )
800
+ .await;
801
+ }
802
+
803
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
804
+ #[tokio::test]
805
+ async fn two_signals(hist_batches: &'static [usize]) {
806
+ let wfid = "fake_wf_id";
807
+
808
+ let t = canned_histories::two_signals("sig1", "sig2");
809
+ let core = build_fake_worker(wfid, t, hist_batches);
810
+
811
+ poll_and_reply(
812
+ &core,
813
+ NonSticky,
814
+ &[
815
+ gen_assert_and_reply(
816
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
817
+ // Task is completed with no commands
818
+ vec![],
819
+ ),
820
+ gen_assert_and_reply(
821
+ &job_assert!(
822
+ workflow_activation_job::Variant::SignalWorkflow(_),
823
+ workflow_activation_job::Variant::SignalWorkflow(_)
824
+ ),
825
+ vec![],
826
+ ),
827
+ ],
828
+ )
829
+ .await;
830
+ }
831
+
832
+ #[tokio::test]
833
+ async fn workflow_failures_only_reported_once() {
834
+ let wfid = "fake_wf_id";
835
+ let timer_1 = 1;
836
+ let timer_2 = 2;
837
+
838
+ let hist = canned_histories::workflow_fails_with_failure_two_different_points(
839
+ timer_1.to_string().as_str(),
840
+ timer_2.to_string().as_str(),
841
+ );
842
+ let response_batches = vec![
843
+ 1, 2, // Start then first good reply
844
+ 2, 2, 2, // Poll for every failure
845
+ // Poll again after evicting after second good reply, then two more fails
846
+ 3, 3, 3,
847
+ ];
848
+ let mocks = build_multihist_mock_sg(
849
+ vec![FakeWfResponses {
850
+ wf_id: wfid.to_string(),
851
+ hist,
852
+ response_batches: response_batches.into_iter().map(Into::into).collect(),
853
+ }],
854
+ true,
855
+ // We should only call the server to say we failed twice (once after each success)
856
+ 2,
857
+ );
858
+ let omap = mocks.outstanding_task_map.clone();
859
+ let core = mock_worker(mocks);
860
+
861
+ poll_and_reply_clears_outstanding_evicts(
862
+ &core,
863
+ omap,
864
+ NonSticky,
865
+ &[
866
+ gen_assert_and_reply(
867
+ &|_| {},
868
+ vec![start_timer_cmd(timer_1, Duration::from_secs(1))],
869
+ ),
870
+ // Fail a few times in a row (only one of which should be reported)
871
+ gen_assert_and_fail(&|_| {}),
872
+ gen_assert_and_fail(&|_| {}),
873
+ gen_assert_and_fail(&|_| {}),
874
+ gen_assert_and_reply(
875
+ &job_assert!(workflow_activation_job::Variant::FireTimer(_)),
876
+ vec![start_timer_cmd(timer_2, Duration::from_secs(1))],
877
+ ),
878
+ // Again (a new fail should be reported here)
879
+ gen_assert_and_fail(&|_| {}),
880
+ gen_assert_and_fail(&|_| {}),
881
+ gen_assert_and_reply(
882
+ &job_assert!(workflow_activation_job::Variant::FireTimer(_)),
883
+ vec![CompleteWorkflowExecution { result: None }.into()],
884
+ ),
885
+ ],
886
+ )
887
+ .await;
888
+ }
889
+
890
+ #[tokio::test]
891
+ async fn max_wft_respected() {
892
+ let total_wfs = 100;
893
+ let wf_ids: Vec<_> = (0..total_wfs)
894
+ .into_iter()
895
+ .map(|i| format!("fake-wf-{}", i))
896
+ .collect();
897
+ let hists = wf_ids.iter().map(|wf_id| {
898
+ let hist = canned_histories::single_timer("1");
899
+ FakeWfResponses {
900
+ wf_id: wf_id.to_string(),
901
+ hist,
902
+ response_batches: vec![1.into(), 2.into()],
903
+ }
904
+ });
905
+ let mh = MockPollCfg::new(hists.into_iter().collect(), true, 0);
906
+ let mut worker = mock_sdk_cfg(mh, |cfg| {
907
+ cfg.max_cached_workflows = total_wfs as usize;
908
+ cfg.max_outstanding_workflow_tasks = 1;
909
+ });
910
+ let active_count: &'static _ = Box::leak(Box::new(Semaphore::new(1)));
911
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, move |ctx: WfContext| async move {
912
+ drop(
913
+ active_count
914
+ .try_acquire()
915
+ .expect("No multiple concurrent workflow tasks!"),
916
+ );
917
+ ctx.timer(Duration::from_secs(1)).await;
918
+ Ok(().into())
919
+ });
920
+
921
+ for wf_id in wf_ids {
922
+ worker
923
+ .submit_wf(wf_id, DEFAULT_WORKFLOW_TYPE, vec![], Default::default())
924
+ .await
925
+ .unwrap();
926
+ }
927
+ worker.run_until_done().await.unwrap();
928
+ }
929
+
930
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[3]))]
931
+ #[tokio::test]
932
+ async fn activity_not_canceled_on_replay_repro(hist_batches: &'static [usize]) {
933
+ let wfid = "fake_wf_id";
934
+ let t = canned_histories::unsent_at_cancel_repro();
935
+ let core = build_fake_worker(wfid, t, hist_batches);
936
+ let activity_id = 1;
937
+
938
+ poll_and_reply(
939
+ &core,
940
+ NonSticky,
941
+ &[
942
+ gen_assert_and_reply(
943
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
944
+ // Start timer and activity
945
+ vec![
946
+ ScheduleActivity {
947
+ seq: activity_id,
948
+ activity_id: activity_id.to_string(),
949
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
950
+ ..Default::default()
951
+ }
952
+ .into(),
953
+ start_timer_cmd(1, Duration::from_secs(1)),
954
+ ],
955
+ ),
956
+ gen_assert_and_reply(
957
+ &job_assert!(workflow_activation_job::Variant::FireTimer(_)),
958
+ vec![RequestCancelActivity { seq: activity_id }.into()],
959
+ ),
960
+ gen_assert_and_reply(
961
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(
962
+ ResolveActivity {
963
+ result: Some(ActivityResolution {
964
+ status: Some(activity_resolution::Status::Cancelled(..)),
965
+ }),
966
+ ..
967
+ }
968
+ )),
969
+ vec![start_timer_cmd(2, Duration::from_secs(1))],
970
+ ),
971
+ ],
972
+ )
973
+ .await;
974
+ }
975
+
976
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[3]))]
977
+ #[tokio::test]
978
+ async fn activity_not_canceled_when_also_completed_repro(hist_batches: &'static [usize]) {
979
+ let wfid = "fake_wf_id";
980
+ let t = canned_histories::cancel_not_sent_when_also_complete_repro();
981
+ let core = build_fake_worker(wfid, t, hist_batches);
982
+ let activity_id = 1;
983
+
984
+ poll_and_reply(
985
+ &core,
986
+ NonSticky,
987
+ &[
988
+ gen_assert_and_reply(
989
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
990
+ vec![ScheduleActivity {
991
+ seq: activity_id,
992
+ activity_id: activity_id.to_string(),
993
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
994
+ ..Default::default()
995
+ }
996
+ .into()],
997
+ ),
998
+ gen_assert_and_reply(
999
+ &job_assert!(workflow_activation_job::Variant::SignalWorkflow(_)),
1000
+ vec![
1001
+ RequestCancelActivity { seq: activity_id }.into(),
1002
+ start_timer_cmd(2, Duration::from_secs(1)),
1003
+ ],
1004
+ ),
1005
+ gen_assert_and_reply(
1006
+ &job_assert!(workflow_activation_job::Variant::ResolveActivity(
1007
+ ResolveActivity {
1008
+ result: Some(ActivityResolution {
1009
+ status: Some(activity_resolution::Status::Cancelled(..)),
1010
+ }),
1011
+ ..
1012
+ }
1013
+ )),
1014
+ vec![CompleteWorkflowExecution { result: None }.into()],
1015
+ ),
1016
+ ],
1017
+ )
1018
+ .await;
1019
+ }
1020
+
1021
+ #[tokio::test]
1022
+ async fn lots_of_workflows() {
1023
+ let total_wfs = 500;
1024
+ let hists = (0..total_wfs).into_iter().map(|i| {
1025
+ let wf_id = format!("fake-wf-{}", i);
1026
+ let hist = canned_histories::single_timer("1");
1027
+ FakeWfResponses {
1028
+ wf_id,
1029
+ hist,
1030
+ response_batches: vec![1.into(), 2.into()],
1031
+ }
1032
+ });
1033
+ let mut mock = build_multihist_mock_sg(hists, false, 0);
1034
+ mock.make_wft_stream_interminable();
1035
+ let worker = &mock_worker(mock);
1036
+ let completed_count = Arc::new(Semaphore::new(0));
1037
+ let killer = async {
1038
+ let _ = completed_count.acquire_many(total_wfs).await.unwrap();
1039
+ worker.initiate_shutdown();
1040
+ };
1041
+ let poller = fanout_tasks(5, |_| {
1042
+ let completed_count = completed_count.clone();
1043
+ async move {
1044
+ while let Ok(wft) = worker.poll_workflow_activation().await {
1045
+ let job = &wft.jobs[0];
1046
+ let reply = match job.variant {
1047
+ Some(workflow_activation_job::Variant::StartWorkflow(_)) => {
1048
+ start_timer_cmd(1, Duration::from_secs(1))
1049
+ }
1050
+ Some(workflow_activation_job::Variant::RemoveFromCache(_)) => {
1051
+ worker
1052
+ .complete_workflow_activation(WorkflowActivationCompletion::empty(
1053
+ wft.run_id,
1054
+ ))
1055
+ .await
1056
+ .unwrap();
1057
+ continue;
1058
+ }
1059
+ _ => {
1060
+ completed_count.add_permits(1);
1061
+ CompleteWorkflowExecution { result: None }.into()
1062
+ }
1063
+ };
1064
+ worker
1065
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1066
+ wft.run_id, reply,
1067
+ ))
1068
+ .await
1069
+ .unwrap();
1070
+ }
1071
+ }
1072
+ });
1073
+ join!(killer, poller);
1074
+ worker.shutdown().await;
1075
+ }
1076
+
1077
+ #[rstest(hist_batches, case::incremental(&[1, 2]), case::replay(&[2]))]
1078
+ #[tokio::test]
1079
+ async fn wft_timeout_repro(hist_batches: &'static [usize]) {
1080
+ let wfid = "fake_wf_id";
1081
+ let t = canned_histories::wft_timeout_repro();
1082
+ let core = build_fake_worker(wfid, t, hist_batches);
1083
+ let activity_id = 1;
1084
+
1085
+ poll_and_reply(
1086
+ &core,
1087
+ NonSticky,
1088
+ &[
1089
+ gen_assert_and_reply(
1090
+ &job_assert!(workflow_activation_job::Variant::StartWorkflow(_)),
1091
+ vec![ScheduleActivity {
1092
+ seq: activity_id,
1093
+ activity_id: activity_id.to_string(),
1094
+ cancellation_type: ActivityCancellationType::TryCancel as i32,
1095
+ ..Default::default()
1096
+ }
1097
+ .into()],
1098
+ ),
1099
+ gen_assert_and_reply(
1100
+ &job_assert!(
1101
+ workflow_activation_job::Variant::SignalWorkflow(_),
1102
+ workflow_activation_job::Variant::SignalWorkflow(_),
1103
+ workflow_activation_job::Variant::ResolveActivity(ResolveActivity {
1104
+ result: Some(ActivityResolution {
1105
+ status: Some(activity_resolution::Status::Completed(..)),
1106
+ }),
1107
+ ..
1108
+ })
1109
+ ),
1110
+ vec![CompleteWorkflowExecution { result: None }.into()],
1111
+ ),
1112
+ ],
1113
+ )
1114
+ .await;
1115
+ }
1116
+
1117
+ #[tokio::test]
1118
+ async fn complete_after_eviction() {
1119
+ let wfid = "fake_wf_id";
1120
+ let t = canned_histories::single_timer("1");
1121
+ let mut mock = mock_workflow_client();
1122
+ mock.expect_complete_workflow_task().times(0);
1123
+ let mock = single_hist_mock_sg(wfid, t, [2], mock, true);
1124
+ let core = mock_worker(mock);
1125
+
1126
+ let activation = core.poll_workflow_activation().await.unwrap();
1127
+ // We just got start workflow, immediately evict
1128
+ core.request_workflow_eviction(&activation.run_id);
1129
+ // Since we got whole history, we must finish replay before eviction will appear
1130
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1131
+ activation.run_id,
1132
+ start_timer_cmd(1, Duration::from_secs(1)),
1133
+ ))
1134
+ .await
1135
+ .unwrap();
1136
+ let next_activation = core.poll_workflow_activation().await.unwrap();
1137
+ assert_matches!(
1138
+ next_activation.jobs.as_slice(),
1139
+ [WorkflowActivationJob {
1140
+ variant: Some(workflow_activation_job::Variant::FireTimer(_)),
1141
+ },]
1142
+ );
1143
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1144
+ next_activation.run_id,
1145
+ vec![CompleteWorkflowExecution { result: None }.into()],
1146
+ ))
1147
+ .await
1148
+ .unwrap();
1149
+
1150
+ core.shutdown().await;
1151
+ }
1152
+
1153
+ #[tokio::test]
1154
+ async fn sends_appropriate_sticky_task_queue_responses() {
1155
+ // This test verifies that when completions are sent with sticky queues enabled, that they
1156
+ // include the information that tells the server to enqueue the next task on a sticky queue.
1157
+ let wfid = "fake_wf_id";
1158
+ let t = canned_histories::single_timer("1");
1159
+ let mut mock = mock_workflow_client();
1160
+ mock.expect_complete_workflow_task()
1161
+ .withf(|comp| comp.sticky_attributes.is_some())
1162
+ .times(1)
1163
+ .returning(|_| Ok(Default::default()));
1164
+ mock.expect_complete_workflow_task().times(0);
1165
+ let mut mock = single_hist_mock_sg(wfid, t, [1], mock, false);
1166
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 10);
1167
+ let core = mock_worker(mock);
1168
+
1169
+ let activation = core.poll_workflow_activation().await.unwrap();
1170
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1171
+ activation.run_id,
1172
+ start_timer_cmd(1, Duration::from_secs(1)),
1173
+ ))
1174
+ .await
1175
+ .unwrap();
1176
+ core.shutdown().await;
1177
+ }
1178
+
1179
+ #[tokio::test]
1180
+ async fn new_server_work_while_eviction_outstanding_doesnt_overwrite_activation() {
1181
+ let wfid = "fake_wf_id";
1182
+ let t = canned_histories::single_timer("1");
1183
+ let mock = single_hist_mock_sg(wfid, t, [1, 2], mock_workflow_client(), false);
1184
+ let taskmap = mock.outstanding_task_map.clone().unwrap();
1185
+ let core = mock_worker(mock);
1186
+
1187
+ // Poll for and complete first workflow task
1188
+ let activation = core.poll_workflow_activation().await.unwrap();
1189
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1190
+ activation.run_id,
1191
+ start_timer_cmd(1, Duration::from_secs(1)),
1192
+ ))
1193
+ .await
1194
+ .unwrap();
1195
+ let evict_act = core.poll_workflow_activation().await.unwrap();
1196
+ assert_matches!(
1197
+ evict_act.jobs.as_slice(),
1198
+ [WorkflowActivationJob {
1199
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
1200
+ }]
1201
+ );
1202
+ // Ensure mock has delivered both tasks
1203
+ assert!(taskmap.all_work_delivered());
1204
+ // Now we can complete the evict
1205
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(evict_act.run_id))
1206
+ .await
1207
+ .unwrap();
1208
+ // The task buffered during eviction is applied and we start over
1209
+ let start_again = core.poll_workflow_activation().await.unwrap();
1210
+ assert_matches!(
1211
+ start_again.jobs[0].variant,
1212
+ Some(workflow_activation_job::Variant::StartWorkflow(_))
1213
+ );
1214
+ }
1215
+
1216
+ #[tokio::test]
1217
+ async fn buffered_work_drained_on_shutdown() {
1218
+ let wfid = "fake_wf_id";
1219
+ // Build a one-timer history where first task times out
1220
+ let mut t = TestHistoryBuilder::default();
1221
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1222
+ t.add_workflow_task_scheduled_and_started();
1223
+ // Need to build the first response before adding the timeout events b/c otherwise the history
1224
+ // builder will include them in the first task
1225
+ let resp_1 = hist_to_poll_resp(&t, wfid.to_owned(), 1.into()).resp;
1226
+ t.add_workflow_task_timed_out();
1227
+ t.add_full_wf_task();
1228
+ let timer_started_event_id = t.add_get_event_id(EventType::TimerStarted, None);
1229
+ t.add(
1230
+ EventType::TimerFired,
1231
+ history_event::Attributes::TimerFiredEventAttributes(TimerFiredEventAttributes {
1232
+ started_event_id: timer_started_event_id,
1233
+ timer_id: "1".to_string(),
1234
+ }),
1235
+ );
1236
+ t.add_full_wf_task();
1237
+ t.add_workflow_execution_completed();
1238
+
1239
+ let mut tasks = VecDeque::from(vec![resp_1]);
1240
+ // Extend the task list with the now timeout-included version of the task. We add a bunch of
1241
+ // them because the poll loop will spin while new tasks are available and it is buffering them
1242
+ tasks.extend(
1243
+ std::iter::repeat_with(|| hist_to_poll_resp(&t, wfid.to_owned(), 2.into()).resp).take(50),
1244
+ );
1245
+ let mut mock = mock_workflow_client();
1246
+ mock.expect_complete_workflow_task()
1247
+ .returning(|_| Ok(RespondWorkflowTaskCompletedResponse::default()));
1248
+ let mut mock = MocksHolder::from_wft_stream(mock, stream::iter(tasks));
1249
+ // Cache on to avoid being super repetitive
1250
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 10);
1251
+ let core = &mock_worker(mock);
1252
+
1253
+ // Poll for first WFT
1254
+ let act1 = core.poll_workflow_activation().await.unwrap();
1255
+ let poll_fut = async move {
1256
+ // Now poll again, which will start spinning, and buffer the next WFT with timer fired in it
1257
+ // - it won't stop spinning until the first task is complete
1258
+ let t = core.poll_workflow_activation().await.unwrap();
1259
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1260
+ t.run_id,
1261
+ vec![CompleteWorkflowExecution { result: None }.into()],
1262
+ ))
1263
+ .await
1264
+ .unwrap();
1265
+ };
1266
+ let complete_first = async move {
1267
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1268
+ act1.run_id,
1269
+ start_timer_cmd(1, Duration::from_secs(1)),
1270
+ ))
1271
+ .await
1272
+ .unwrap();
1273
+ };
1274
+ join!(poll_fut, complete_first, async {
1275
+ // If the shutdown is sent too too fast, we might not have got a chance to even buffer work
1276
+ tokio::time::sleep(Duration::from_millis(5)).await;
1277
+ core.shutdown().await;
1278
+ });
1279
+ }
1280
+
1281
+ #[tokio::test]
1282
+ async fn fail_wft_then_recover() {
1283
+ let t = canned_histories::long_sequential_timers(1);
1284
+ let mut mh = MockPollCfg::from_resp_batches(
1285
+ "fake_wf_id",
1286
+ t,
1287
+ // We need to deliver all of history twice because of eviction
1288
+ [ResponseType::AllHistory, ResponseType::AllHistory],
1289
+ mock_workflow_client(),
1290
+ );
1291
+ mh.num_expected_fails = 1;
1292
+ mh.expect_fail_wft_matcher =
1293
+ Box::new(|_, cause, _| matches!(cause, WorkflowTaskFailedCause::NonDeterministicError));
1294
+ let mut mock = build_mock_pollers(mh);
1295
+ mock.worker_cfg(|wc| {
1296
+ wc.max_cached_workflows = 2;
1297
+ });
1298
+ let core = mock_worker(mock);
1299
+
1300
+ let act = core.poll_workflow_activation().await.unwrap();
1301
+ // Start an activity instead of a timer, triggering nondeterminism error
1302
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1303
+ act.run_id.clone(),
1304
+ vec![ScheduleActivity {
1305
+ activity_id: "fake_activity".to_string(),
1306
+ ..Default::default()
1307
+ }
1308
+ .into()],
1309
+ ))
1310
+ .await
1311
+ .unwrap();
1312
+ // We must handle an eviction now
1313
+ let evict_act = core.poll_workflow_activation().await.unwrap();
1314
+ assert_eq!(evict_act.run_id, act.run_id);
1315
+ assert_matches!(
1316
+ evict_act.jobs.as_slice(),
1317
+ [WorkflowActivationJob {
1318
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
1319
+ }]
1320
+ );
1321
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(evict_act.run_id))
1322
+ .await
1323
+ .unwrap();
1324
+
1325
+ // Workflow starting over, this time issue the right command
1326
+ let act = core.poll_workflow_activation().await.unwrap();
1327
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1328
+ act.run_id,
1329
+ vec![start_timer_cmd(1, Duration::from_secs(1))],
1330
+ ))
1331
+ .await
1332
+ .unwrap();
1333
+ let act = core.poll_workflow_activation().await.unwrap();
1334
+ assert_matches!(
1335
+ act.jobs.as_slice(),
1336
+ [WorkflowActivationJob {
1337
+ variant: Some(workflow_activation_job::Variant::FireTimer(_)),
1338
+ },]
1339
+ );
1340
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1341
+ act.run_id,
1342
+ vec![CompleteWorkflowExecution { result: None }.into()],
1343
+ ))
1344
+ .await
1345
+ .unwrap();
1346
+ core.shutdown().await;
1347
+ }
1348
+
1349
+ #[tokio::test]
1350
+ async fn poll_response_triggers_wf_error() {
1351
+ let mut t = TestHistoryBuilder::default();
1352
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1353
+ // Add this nonsense event here to make applying the poll response fail
1354
+ t.add_external_signal_completed(100);
1355
+ t.add_full_wf_task();
1356
+ t.add_workflow_execution_completed();
1357
+
1358
+ let mh = MockPollCfg::from_resp_batches(
1359
+ "fake_wf_id",
1360
+ t,
1361
+ [ResponseType::AllHistory],
1362
+ mock_workflow_client(),
1363
+ );
1364
+ let mock = build_mock_pollers(mh);
1365
+ let core = mock_worker(mock);
1366
+ // Poll for first WFT, which is immediately an eviction
1367
+ let act = core.poll_workflow_activation().await.unwrap();
1368
+ assert_matches!(
1369
+ act.jobs.as_slice(),
1370
+ [WorkflowActivationJob {
1371
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
1372
+ }]
1373
+ );
1374
+ }
1375
+
1376
+ // Verifies we can handle multiple wft timeouts in a row if lang is being very slow in responding
1377
+ #[tokio::test]
1378
+ async fn lang_slower_than_wft_timeouts() {
1379
+ let wfid = "fake_wf_id";
1380
+ let mut t = TestHistoryBuilder::default();
1381
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1382
+ t.add_workflow_task_scheduled_and_started();
1383
+ t.add_workflow_task_timed_out();
1384
+ t.add_full_wf_task();
1385
+ t.add_workflow_execution_completed();
1386
+
1387
+ let mut mock = mock_workflow_client();
1388
+ mock.expect_complete_workflow_task()
1389
+ .times(1)
1390
+ .returning(|_| Err(tonic::Status::not_found("Workflow task not found.")));
1391
+ mock.expect_complete_workflow_task()
1392
+ .times(1)
1393
+ .returning(|_| Ok(Default::default()));
1394
+ let mut mock = single_hist_mock_sg(wfid, t, [1, 1], mock, true);
1395
+ let tasksmap = mock.outstanding_task_map.clone().unwrap();
1396
+ mock.worker_cfg(|wc| {
1397
+ wc.max_cached_workflows = 2;
1398
+ });
1399
+ let core = mock_worker(mock);
1400
+
1401
+ // This completion runs into the workflow task not found error
1402
+ let wf_task = core.poll_workflow_activation().await.unwrap();
1403
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(wf_task.run_id))
1404
+ .await
1405
+ .unwrap();
1406
+ // It will get an eviction
1407
+ let wf_task = core.poll_workflow_activation().await.unwrap();
1408
+ assert_matches!(
1409
+ wf_task.jobs.as_slice(),
1410
+ [WorkflowActivationJob {
1411
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
1412
+ }]
1413
+ );
1414
+ // Before we complete, unlock the next task from the mock so that we'll see it get buffered.
1415
+ tasksmap.release_run(&wf_task.run_id);
1416
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(wf_task.run_id))
1417
+ .await
1418
+ .unwrap();
1419
+ // The buffered WFT should be applied now
1420
+ let start_again = core.poll_workflow_activation().await.unwrap();
1421
+ assert_matches!(
1422
+ start_again.jobs[0].variant,
1423
+ Some(workflow_activation_job::Variant::StartWorkflow(_))
1424
+ );
1425
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1426
+ start_again.run_id,
1427
+ vec![CompleteWorkflowExecution { result: None }.into()],
1428
+ ))
1429
+ .await
1430
+ .unwrap();
1431
+ core.shutdown().await;
1432
+ }
1433
+
1434
+ #[tokio::test]
1435
+ async fn tries_cancel_of_completed_activity() {
1436
+ let mut t = TestHistoryBuilder::default();
1437
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1438
+ t.add_full_wf_task();
1439
+ let scheduled_event_id = t.add_activity_task_scheduled("1");
1440
+ t.add_we_signaled("sig", vec![]);
1441
+ let started_event_id = t.add_activity_task_started(scheduled_event_id);
1442
+ t.add_activity_task_completed(scheduled_event_id, started_event_id, Default::default());
1443
+ t.add_workflow_task_scheduled_and_started();
1444
+
1445
+ let mock = mock_workflow_client();
1446
+ let mut mock = single_hist_mock_sg("fake_wf_id", t, [1, 2], mock, true);
1447
+ mock.worker_cfg(|cfg| cfg.max_cached_workflows = 1);
1448
+ let core = mock_worker(mock);
1449
+
1450
+ let activation = core.poll_workflow_activation().await.unwrap();
1451
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1452
+ activation.run_id,
1453
+ ScheduleActivity {
1454
+ seq: 1,
1455
+ activity_id: "1".to_string(),
1456
+ ..Default::default()
1457
+ }
1458
+ .into(),
1459
+ ))
1460
+ .await
1461
+ .unwrap();
1462
+ let activation = core.poll_workflow_activation().await.unwrap();
1463
+ assert_matches!(
1464
+ activation.jobs.as_slice(),
1465
+ [
1466
+ WorkflowActivationJob {
1467
+ variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
1468
+ },
1469
+ WorkflowActivationJob {
1470
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(_)),
1471
+ }
1472
+ ]
1473
+ );
1474
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1475
+ activation.run_id,
1476
+ vec![
1477
+ RequestCancelActivity { seq: 1 }.into(),
1478
+ CompleteWorkflowExecution { result: None }.into(),
1479
+ ],
1480
+ ))
1481
+ .await
1482
+ .unwrap();
1483
+
1484
+ core.shutdown().await;
1485
+ }
1486
+
1487
+ #[tokio::test]
1488
+ async fn failing_wft_doesnt_eat_permit_forever() {
1489
+ let mut t = TestHistoryBuilder::default();
1490
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1491
+ t.add_workflow_task_scheduled_and_started();
1492
+
1493
+ let mock = mock_workflow_client();
1494
+ let mut mock = MockPollCfg::from_resp_batches("fake_wf_id", t, [1, 1, 1], mock);
1495
+ mock.num_expected_fails = 1;
1496
+ let mut mock = build_mock_pollers(mock);
1497
+ mock.worker_cfg(|cfg| {
1498
+ cfg.max_cached_workflows = 2;
1499
+ cfg.max_outstanding_workflow_tasks = 2;
1500
+ });
1501
+ let outstanding_mock_tasks = mock.outstanding_task_map.clone();
1502
+ let worker = mock_worker(mock);
1503
+
1504
+ let mut run_id = "".to_string();
1505
+ // Fail twice, verifying a permit is not eaten. We cannot fail the same run more than twice in a
1506
+ // row because we purposefully time out rather than spamming.
1507
+ for _ in 1..=2 {
1508
+ let activation = worker.poll_workflow_activation().await.unwrap();
1509
+ // Issue a nonsense completion that will trigger a WFT failure
1510
+ worker
1511
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1512
+ activation.run_id,
1513
+ RequestCancelActivity { seq: 1 }.into(),
1514
+ ))
1515
+ .await
1516
+ .unwrap();
1517
+ let activation = worker.poll_workflow_activation().await.unwrap();
1518
+ assert_matches!(
1519
+ activation.jobs.as_slice(),
1520
+ [WorkflowActivationJob {
1521
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
1522
+ },]
1523
+ );
1524
+ run_id = activation.run_id.clone();
1525
+ worker
1526
+ .complete_workflow_activation(WorkflowActivationCompletion::empty(activation.run_id))
1527
+ .await
1528
+ .unwrap();
1529
+ }
1530
+ assert_eq!(worker.outstanding_workflow_tasks().await, 0);
1531
+ // 1 permit is in use because the next task is buffered and has re-used the permit
1532
+ assert_eq!(worker.available_wft_permits().await, 1);
1533
+ // We should be "out of work" because the mock service thinks we didn't complete the last task,
1534
+ // which we didn't, because we don't spam failures. The real server would eventually time out
1535
+ // the task. Mock doesn't understand that, so the WFT permit is released because eventually a
1536
+ // new one will be generated. We manually clear the mock's outstanding task list so the next
1537
+ // poll will work.
1538
+ outstanding_mock_tasks.unwrap().release_run(&run_id);
1539
+ let activation = worker.poll_workflow_activation().await.unwrap();
1540
+ // There should be no change in permits, since this just unbuffered the buffered task
1541
+ assert_eq!(worker.available_wft_permits().await, 1);
1542
+ worker
1543
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1544
+ activation.run_id,
1545
+ CompleteWorkflowExecution { result: None }.into(),
1546
+ ))
1547
+ .await
1548
+ .unwrap();
1549
+ assert_eq!(worker.available_wft_permits().await, 2);
1550
+
1551
+ worker.shutdown().await;
1552
+ }
1553
+
1554
+ #[tokio::test]
1555
+ async fn cache_miss_will_fetch_history() {
1556
+ let mut t = TestHistoryBuilder::default();
1557
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1558
+ t.add_full_wf_task();
1559
+ t.add_we_signaled("sig", vec![]);
1560
+ t.add_full_wf_task();
1561
+ t.add_workflow_execution_completed();
1562
+ let get_exec_resp: GetWorkflowExecutionHistoryResponse = t.get_history_info(2).unwrap().into();
1563
+
1564
+ let mut mh = MockPollCfg::from_resp_batches(
1565
+ "fake_wf_id",
1566
+ t,
1567
+ [ResponseType::ToTaskNum(1), ResponseType::OneTask(2)],
1568
+ mock_workflow_client(),
1569
+ );
1570
+ mh.mock_client
1571
+ .expect_get_workflow_execution_history()
1572
+ .times(1)
1573
+ .returning(move |_, _, _| Ok(get_exec_resp.clone()));
1574
+ let mut mock = build_mock_pollers(mh);
1575
+ mock.worker_cfg(|cfg| {
1576
+ cfg.max_cached_workflows = 1;
1577
+ });
1578
+ let worker = mock_worker(mock);
1579
+
1580
+ let activation = worker.poll_workflow_activation().await.unwrap();
1581
+ assert_eq!(activation.history_length, 3);
1582
+ assert_matches!(
1583
+ activation.jobs.as_slice(),
1584
+ [WorkflowActivationJob {
1585
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
1586
+ }]
1587
+ );
1588
+ // Force an eviction (before complete matters, so that we will be sure the eviction is queued
1589
+ // up before the next fake WFT is unlocked)
1590
+ worker.request_wf_eviction(
1591
+ &activation.run_id,
1592
+ "whatever",
1593
+ EvictionReason::LangRequested,
1594
+ );
1595
+ worker
1596
+ .complete_workflow_activation(WorkflowActivationCompletion::empty(&activation.run_id))
1597
+ .await
1598
+ .unwrap();
1599
+ // Handle the eviction, and the restart
1600
+ for i in 1..=2 {
1601
+ let activation = worker.poll_workflow_activation().await.unwrap();
1602
+ assert_eq!(activation.history_length, 3);
1603
+ if i == 1 {
1604
+ assert_matches!(
1605
+ activation.jobs.as_slice(),
1606
+ [WorkflowActivationJob {
1607
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
1608
+ }]
1609
+ );
1610
+ } else {
1611
+ assert_matches!(
1612
+ activation.jobs.as_slice(),
1613
+ [WorkflowActivationJob {
1614
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
1615
+ }]
1616
+ );
1617
+ }
1618
+ worker
1619
+ .complete_workflow_activation(WorkflowActivationCompletion::empty(activation.run_id))
1620
+ .await
1621
+ .unwrap();
1622
+ }
1623
+ let activation = worker.poll_workflow_activation().await.unwrap();
1624
+ assert_eq!(activation.history_length, 7);
1625
+ assert_matches!(
1626
+ activation.jobs.as_slice(),
1627
+ [WorkflowActivationJob {
1628
+ variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
1629
+ }]
1630
+ );
1631
+ worker
1632
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1633
+ activation.run_id,
1634
+ CompleteWorkflowExecution { result: None }.into(),
1635
+ ))
1636
+ .await
1637
+ .unwrap();
1638
+ assert_eq!(worker.outstanding_workflow_tasks().await, 0);
1639
+ worker.shutdown().await;
1640
+ }
1641
+
1642
+ /// This test verifies that WFTs which come as replies to completing a WFT are properly delivered
1643
+ /// via activation polling.
1644
+ #[tokio::test]
1645
+ async fn tasks_from_completion_are_delivered() {
1646
+ let wfid = "fake_wf_id";
1647
+ let mut t = TestHistoryBuilder::default();
1648
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1649
+ t.add_full_wf_task();
1650
+ t.add_we_signaled("sig", vec![]);
1651
+ t.add_full_wf_task();
1652
+ t.add_workflow_execution_completed();
1653
+
1654
+ let mut mock = mock_workflow_client();
1655
+ let complete_resp = RespondWorkflowTaskCompletedResponse {
1656
+ workflow_task: Some(hist_to_poll_resp(&t, wfid.to_owned(), 2.into()).resp),
1657
+ activity_tasks: vec![],
1658
+ reset_history_event_id: 0,
1659
+ };
1660
+ mock.expect_complete_workflow_task()
1661
+ .times(1)
1662
+ .returning(move |_| Ok(complete_resp.clone()));
1663
+ mock.expect_complete_workflow_task()
1664
+ .times(1)
1665
+ .returning(|_| Ok(Default::default()));
1666
+ let mut mock = single_hist_mock_sg(wfid, t, [1], mock, true);
1667
+ mock.worker_cfg(|wc| wc.max_cached_workflows = 2);
1668
+ let core = mock_worker(mock);
1669
+
1670
+ let wf_task = core.poll_workflow_activation().await.unwrap();
1671
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(wf_task.run_id))
1672
+ .await
1673
+ .unwrap();
1674
+ let wf_task = core.poll_workflow_activation().await.unwrap();
1675
+ assert_matches!(
1676
+ wf_task.jobs.as_slice(),
1677
+ [WorkflowActivationJob {
1678
+ variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
1679
+ },]
1680
+ );
1681
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1682
+ wf_task.run_id,
1683
+ vec![CompleteWorkflowExecution { result: None }.into()],
1684
+ ))
1685
+ .await
1686
+ .unwrap();
1687
+ core.shutdown().await;
1688
+ }
1689
+
1690
+ #[tokio::test]
1691
+ async fn poll_faster_than_complete_wont_overflow_cache() {
1692
+ // Make workflow tasks for 5 different runs
1693
+ let tasks: Vec<_> = (1..=5)
1694
+ .map(|i| FakeWfResponses {
1695
+ wf_id: format!("wf-{}", i),
1696
+ hist: canned_histories::single_timer("1"),
1697
+ response_batches: vec![ResponseType::ToTaskNum(1)],
1698
+ })
1699
+ .collect();
1700
+ let mut mock_client = mock_workflow_client();
1701
+ mock_client
1702
+ .expect_complete_workflow_task()
1703
+ .times(3)
1704
+ .returning(|_| Ok(Default::default()));
1705
+ let mut mock_cfg = MockPollCfg::new(tasks, true, 0);
1706
+ mock_cfg.mock_client = mock_client;
1707
+ let mut mock = build_mock_pollers(mock_cfg);
1708
+ mock.worker_cfg(|wc| {
1709
+ wc.max_cached_workflows = 3;
1710
+ wc.max_outstanding_workflow_tasks = 3;
1711
+ });
1712
+ let core = mock_worker(mock);
1713
+ // Poll 4 times, completing once, such that max tasks are never exceeded
1714
+ let p1 = core.poll_workflow_activation().await.unwrap();
1715
+ let p2 = core.poll_workflow_activation().await.unwrap();
1716
+ let p3 = core.poll_workflow_activation().await.unwrap();
1717
+ for (i, p_res) in [&p1, &p2, &p3].into_iter().enumerate() {
1718
+ assert_matches!(
1719
+ &p_res.jobs[0].variant,
1720
+ Some(workflow_activation_job::Variant::StartWorkflow(sw))
1721
+ if sw.workflow_id == format!("wf-{}", i + 1)
1722
+ );
1723
+ }
1724
+ // Complete first task to free a wft slot. Cache size is at 3
1725
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1726
+ p1.run_id,
1727
+ start_timer_cmd(1, Duration::from_secs(1)),
1728
+ ))
1729
+ .await
1730
+ .unwrap();
1731
+ // Now we're at cache limit. We will poll for a task, discover it is for a new run, issue
1732
+ // an eviction, and buffer the new run task. However, the run we're trying to evict has pending
1733
+ // activations! Thus, we must complete them first before this poll will unblock, and then it
1734
+ // will unblock with the eviciton.
1735
+ let p4 = core.poll_workflow_activation();
1736
+ // Make sure the task gets buffered before we start the complete, so the LRU list is in the
1737
+ // expected order and what we expect to evict will be evicted.
1738
+ advance_fut!(p4);
1739
+ let p4 = async {
1740
+ let p4 = p4.await.unwrap();
1741
+ assert_matches!(
1742
+ &p4.jobs.as_slice(),
1743
+ [WorkflowActivationJob {
1744
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
1745
+ }]
1746
+ );
1747
+ p4
1748
+ };
1749
+ let p2_pending_completer = async {
1750
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1751
+ p2.run_id,
1752
+ start_timer_cmd(1, Duration::from_secs(1)),
1753
+ ))
1754
+ .await
1755
+ .unwrap();
1756
+ };
1757
+ let (p4, _) = join!(p4, p2_pending_completer);
1758
+ assert_eq!(core.cached_workflows().await, 3);
1759
+
1760
+ // This poll should also block until the eviction is actually completed
1761
+ let blocking_poll = async {
1762
+ let res = core.poll_workflow_activation().await.unwrap();
1763
+ assert_matches!(
1764
+ &res.jobs[0].variant,
1765
+ Some(workflow_activation_job::Variant::StartWorkflow(sw))
1766
+ if sw.workflow_id == format!("wf-{}", 4)
1767
+ );
1768
+ res
1769
+ };
1770
+ let complete_evict = async {
1771
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(p4.run_id))
1772
+ .await
1773
+ .unwrap();
1774
+ };
1775
+
1776
+ let (_p5, _) = join!(blocking_poll, complete_evict);
1777
+ assert_eq!(core.cached_workflows().await, 3);
1778
+ // The next poll will get an buffer a task for a new run, and generate an eviction for p3 but
1779
+ // that eviction cannot be obtained until we complete the existing outstanding task.
1780
+ let p6 = async {
1781
+ let p6 = core.poll_workflow_activation().await.unwrap();
1782
+ assert_matches!(
1783
+ p6.jobs.as_slice(),
1784
+ [WorkflowActivationJob {
1785
+ variant: Some(workflow_activation_job::Variant::RemoveFromCache(_)),
1786
+ }]
1787
+ );
1788
+ p6
1789
+ };
1790
+ let completer = async {
1791
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1792
+ p3.run_id,
1793
+ start_timer_cmd(1, Duration::from_secs(1)),
1794
+ ))
1795
+ .await
1796
+ .unwrap();
1797
+ };
1798
+ let (p6, _) = join!(p6, completer);
1799
+ let complete_evict = async {
1800
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(p6.run_id))
1801
+ .await
1802
+ .unwrap();
1803
+ };
1804
+ let blocking_poll = async {
1805
+ // This poll will also block until the last eviction goes through, and when it does it'll
1806
+ // produce the final start workflow task
1807
+ let res = core.poll_workflow_activation().await.unwrap();
1808
+ assert_matches!(
1809
+ &res.jobs[0].variant,
1810
+ Some(workflow_activation_job::Variant::StartWorkflow(sw))
1811
+ if sw.workflow_id == "wf-5"
1812
+ );
1813
+ };
1814
+
1815
+ join!(blocking_poll, complete_evict);
1816
+ // p5 outstanding and final poll outstanding -- hence one permit available
1817
+ assert_eq!(core.available_wft_permits().await, 1);
1818
+ assert_eq!(core.cached_workflows().await, 3);
1819
+ }
1820
+
1821
+ #[tokio::test]
1822
+ async fn eviction_waits_until_replay_finished() {
1823
+ let wfid = "fake_wf_id";
1824
+ let t = canned_histories::long_sequential_timers(3);
1825
+ let mock = mock_workflow_client();
1826
+ let mock = single_hist_mock_sg(wfid, t, [3], mock, true);
1827
+ let core = mock_worker(mock);
1828
+
1829
+ let activation = core.poll_workflow_activation().await.unwrap();
1830
+ assert_eq!(activation.history_length, 3);
1831
+ // Immediately request eviction after getting start workflow
1832
+ core.request_workflow_eviction(&activation.run_id);
1833
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1834
+ activation.run_id,
1835
+ start_timer_cmd(1, Duration::from_secs(1)),
1836
+ ))
1837
+ .await
1838
+ .unwrap();
1839
+ let t1_fired = core.poll_workflow_activation().await.unwrap();
1840
+ assert_eq!(t1_fired.history_length, 8);
1841
+ assert_matches!(
1842
+ t1_fired.jobs.as_slice(),
1843
+ [WorkflowActivationJob {
1844
+ variant: Some(workflow_activation_job::Variant::FireTimer(_)),
1845
+ }]
1846
+ );
1847
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1848
+ t1_fired.run_id,
1849
+ start_timer_cmd(2, Duration::from_secs(1)),
1850
+ ))
1851
+ .await
1852
+ .unwrap();
1853
+ let t2_fired = core.poll_workflow_activation().await.unwrap();
1854
+ assert_eq!(t2_fired.history_length, 13);
1855
+ assert_matches!(
1856
+ t2_fired.jobs.as_slice(),
1857
+ [WorkflowActivationJob {
1858
+ variant: Some(workflow_activation_job::Variant::FireTimer(_)),
1859
+ }]
1860
+ );
1861
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmds(
1862
+ t2_fired.run_id,
1863
+ vec![CompleteWorkflowExecution { result: None }.into()],
1864
+ ))
1865
+ .await
1866
+ .unwrap();
1867
+
1868
+ core.shutdown().await;
1869
+ }
1870
+
1871
+ #[tokio::test]
1872
+ async fn autocompletes_wft_no_work() {
1873
+ let wfid = "fake_wf_id";
1874
+ let activity_id = "1";
1875
+
1876
+ let mut t = TestHistoryBuilder::default();
1877
+ t.add_by_type(EventType::WorkflowExecutionStarted);
1878
+ t.add_full_wf_task();
1879
+ let scheduled_event_id = t.add_activity_task_scheduled(activity_id);
1880
+ t.add_full_wf_task();
1881
+ t.add_we_signaled("sig1", vec![]);
1882
+ t.add_full_wf_task();
1883
+ let started_event_id = t.add_activity_task_started(scheduled_event_id);
1884
+ t.add_activity_task_completed(scheduled_event_id, started_event_id, Default::default());
1885
+ t.add_full_wf_task();
1886
+ let mock = mock_workflow_client();
1887
+ let mut mock = single_hist_mock_sg(wfid, t, [1, 2, 3, 4], mock, true);
1888
+ mock.worker_cfg(|w| w.max_cached_workflows = 1);
1889
+ let core = mock_worker(mock);
1890
+
1891
+ let act = core.poll_workflow_activation().await.unwrap();
1892
+ assert_matches!(
1893
+ act.jobs.as_slice(),
1894
+ [WorkflowActivationJob {
1895
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
1896
+ }]
1897
+ );
1898
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1899
+ act.run_id,
1900
+ ScheduleActivity {
1901
+ seq: 1,
1902
+ activity_id: activity_id.to_string(),
1903
+ cancellation_type: ActivityCancellationType::Abandon as i32,
1904
+ ..Default::default()
1905
+ }
1906
+ .into(),
1907
+ ))
1908
+ .await
1909
+ .unwrap();
1910
+ let act = core.poll_workflow_activation().await.unwrap();
1911
+ assert_matches!(
1912
+ act.jobs.as_slice(),
1913
+ [WorkflowActivationJob {
1914
+ variant: Some(workflow_activation_job::Variant::SignalWorkflow(_)),
1915
+ }]
1916
+ );
1917
+ core.complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1918
+ act.run_id,
1919
+ RequestCancelActivity { seq: 1 }.into(),
1920
+ ))
1921
+ .await
1922
+ .unwrap();
1923
+ let act = core.poll_workflow_activation().await.unwrap();
1924
+ core.complete_workflow_activation(WorkflowActivationCompletion::empty(act.run_id))
1925
+ .await
1926
+ .unwrap();
1927
+ // The last task will autocomplete, and thus this will return shutdown since there is no more
1928
+ // work
1929
+ assert_matches!(
1930
+ core.poll_workflow_activation().await.unwrap_err(),
1931
+ PollWfError::ShutDown
1932
+ );
1933
+
1934
+ core.shutdown().await;
1935
+ }
1936
+
1937
+ #[tokio::test]
1938
+ async fn no_race_acquiring_permits() {
1939
+ let wfid = "fake_wf_id";
1940
+ let mut mock_client = mock_manual_workflow_client();
1941
+ // We need to allow two polls to happen by triggering two processing events in the workflow
1942
+ // stream, but then delivering the actual tasks after that
1943
+ let task_barr: &'static Barrier = Box::leak(Box::new(Barrier::new(2)));
1944
+ mock_client
1945
+ .expect_poll_workflow_task()
1946
+ .returning(move |_, _| {
1947
+ let t = canned_histories::single_timer("1");
1948
+ let poll_resp = hist_to_poll_resp(&t, wfid.to_owned(), 2.into()).resp;
1949
+ async move {
1950
+ task_barr.wait().await;
1951
+ Ok(poll_resp.clone())
1952
+ }
1953
+ .boxed()
1954
+ });
1955
+ mock_client
1956
+ .expect_complete_workflow_task()
1957
+ .returning(|_| async move { Ok(Default::default()) }.boxed());
1958
+
1959
+ let worker = Worker::new_test(
1960
+ test_worker_cfg()
1961
+ .max_outstanding_workflow_tasks(1_usize)
1962
+ .max_cached_workflows(10_usize)
1963
+ .build()
1964
+ .unwrap(),
1965
+ mock_client,
1966
+ );
1967
+
1968
+ // Two polls in a row, both of which will get stuck on the barrier and are only allowed to
1969
+ // proceed after a call which will cause the workflow stream to process an event. Without the
1970
+ // fix, this would've meant the stream though it was OK to poll twice, but once the tasks
1971
+ // are received, it would find there was only one permit.
1972
+ let poll_1_f = async {
1973
+ let r = worker.poll_workflow_activation().await.unwrap();
1974
+ worker
1975
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1976
+ r.run_id,
1977
+ start_timer_cmd(1, Duration::from_secs(1)),
1978
+ ))
1979
+ .await
1980
+ .unwrap();
1981
+ };
1982
+ let poll_2_f = async {
1983
+ let r = worker.poll_workflow_activation().await.unwrap();
1984
+ worker
1985
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
1986
+ r.run_id,
1987
+ start_timer_cmd(1, Duration::from_secs(1)),
1988
+ ))
1989
+ .await
1990
+ .unwrap();
1991
+ };
1992
+ let other_f = async {
1993
+ worker.cached_workflows().await;
1994
+ task_barr.wait().await;
1995
+ worker.cached_workflows().await;
1996
+ task_barr.wait().await;
1997
+ };
1998
+ join!(poll_1_f, poll_2_f, other_f);
1999
+ }
2000
+
2001
+ #[tokio::test]
2002
+ async fn continue_as_new_preserves_some_values() {
2003
+ let wfid = "fake_wf_id";
2004
+ let memo = HashMap::<String, Payload>::from([("enchi".to_string(), b"cat".into())]).into();
2005
+ let search = HashMap::<String, Payload>::from([("noisy".to_string(), b"kitty".into())]).into();
2006
+ let retry_policy = RetryPolicy {
2007
+ backoff_coefficient: 13.37,
2008
+ ..Default::default()
2009
+ };
2010
+ let mut wes_attrs = default_wes_attribs();
2011
+ wes_attrs.memo = Some(memo);
2012
+ wes_attrs.search_attributes = Some(search);
2013
+ wes_attrs.retry_policy = Some(retry_policy);
2014
+ let mut mock_client = mock_workflow_client();
2015
+ let hist = {
2016
+ let mut t = TestHistoryBuilder::default();
2017
+ t.add(
2018
+ EventType::WorkflowExecutionStarted,
2019
+ wes_attrs.clone().into(),
2020
+ );
2021
+ t.add_full_wf_task();
2022
+ t
2023
+ };
2024
+ mock_client
2025
+ .expect_poll_workflow_task()
2026
+ .returning(move |_, _| {
2027
+ Ok(hist_to_poll_resp(&hist, wfid.to_owned(), ResponseType::AllHistory).resp)
2028
+ });
2029
+ mock_client
2030
+ .expect_complete_workflow_task()
2031
+ .returning(move |mut c| {
2032
+ let can_cmd = c.commands.pop().unwrap().attributes.unwrap();
2033
+ if let Attributes::ContinueAsNewWorkflowExecutionCommandAttributes(a) = can_cmd {
2034
+ assert_eq!(a.workflow_type.unwrap().name, "meow");
2035
+ assert_eq!(a.memo, wes_attrs.memo);
2036
+ assert_eq!(a.search_attributes, wes_attrs.search_attributes);
2037
+ assert_eq!(a.retry_policy, wes_attrs.retry_policy);
2038
+ } else {
2039
+ panic!("Wrong attributes type");
2040
+ }
2041
+ Ok(Default::default())
2042
+ });
2043
+
2044
+ let worker = Worker::new_test(test_worker_cfg().build().unwrap(), mock_client);
2045
+ let r = worker.poll_workflow_activation().await.unwrap();
2046
+ worker
2047
+ .complete_workflow_activation(WorkflowActivationCompletion::from_cmd(
2048
+ r.run_id,
2049
+ ContinueAsNewWorkflowExecution {
2050
+ workflow_type: "meow".to_string(),
2051
+ ..Default::default()
2052
+ }
2053
+ .into(),
2054
+ ))
2055
+ .await
2056
+ .unwrap();
2057
+ }
2058
+
2059
+ #[rstest]
2060
+ #[tokio::test]
2061
+ async fn ignorable_events_are_ok(#[values(true, false)] attribs_unset: bool) {
2062
+ let mut t = TestHistoryBuilder::default();
2063
+ t.add_by_type(EventType::WorkflowExecutionStarted);
2064
+ let id = t.add_get_event_id(
2065
+ EventType::Unspecified,
2066
+ Some(
2067
+ history_event::Attributes::WorkflowPropertiesModifiedExternallyEventAttributes(
2068
+ Default::default(),
2069
+ ),
2070
+ ),
2071
+ );
2072
+ t.modify_event(id, |e| e.worker_may_ignore = true);
2073
+ if attribs_unset {
2074
+ t.modify_event(id, |e| {
2075
+ e.event_type = EventType::WorkflowPropertiesModifiedExternally as i32;
2076
+ e.attributes = None;
2077
+ });
2078
+ }
2079
+ t.add_workflow_task_scheduled_and_started();
2080
+
2081
+ let mock = mock_workflow_client();
2082
+ let mock = single_hist_mock_sg("wheee", t, [ResponseType::AllHistory], mock, true);
2083
+ let core = mock_worker(mock);
2084
+
2085
+ let act = core.poll_workflow_activation().await.unwrap();
2086
+ assert_matches!(
2087
+ act.jobs[0].variant,
2088
+ Some(workflow_activation_job::Variant::StartWorkflow(_))
2089
+ );
2090
+ }