@temporalio/core-bridge 1.14.2-canary-release-testing.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/Cargo.lock +794 -650
  2. package/bridge-macros/src/derive_tryintojs.rs +40 -0
  3. package/lib/native.d.ts +24 -3
  4. package/package.json +4 -4
  5. package/releases/aarch64-apple-darwin/index.node +0 -0
  6. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  7. package/releases/x86_64-apple-darwin/index.node +0 -0
  8. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  9. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  10. package/sdk-core/.github/workflows/per-pr.yml +6 -6
  11. package/sdk-core/AGENTS.md +42 -31
  12. package/sdk-core/Cargo.toml +4 -1
  13. package/sdk-core/README.md +19 -13
  14. package/sdk-core/crates/client/Cargo.toml +4 -0
  15. package/sdk-core/crates/client/README.md +139 -0
  16. package/sdk-core/crates/client/src/async_activity_handle.rs +297 -0
  17. package/sdk-core/crates/client/src/callback_based.rs +7 -0
  18. package/sdk-core/crates/client/src/errors.rs +294 -0
  19. package/sdk-core/crates/client/src/{raw.rs → grpc.rs} +370 -159
  20. package/sdk-core/crates/client/src/lib.rs +920 -1326
  21. package/sdk-core/crates/client/src/metrics.rs +24 -33
  22. package/sdk-core/crates/client/src/options_structs.rs +457 -0
  23. package/sdk-core/crates/client/src/replaceable.rs +5 -4
  24. package/sdk-core/crates/client/src/request_extensions.rs +8 -9
  25. package/sdk-core/crates/client/src/retry.rs +99 -54
  26. package/sdk-core/crates/client/src/{worker/mod.rs → worker.rs} +104 -29
  27. package/sdk-core/crates/client/src/workflow_handle.rs +826 -0
  28. package/sdk-core/crates/common/Cargo.toml +62 -3
  29. package/sdk-core/crates/common/build.rs +742 -12
  30. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/ci.yml +2 -0
  31. package/sdk-core/crates/common/protos/api_upstream/.github/workflows/create-release.yml +0 -5
  32. package/sdk-core/crates/common/protos/api_upstream/Makefile +2 -1
  33. package/sdk-core/crates/common/protos/api_upstream/README.md +8 -0
  34. package/sdk-core/crates/common/protos/api_upstream/cmd/check-path-conflicts/main.go +137 -0
  35. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv2.json +3329 -2647
  36. package/sdk-core/crates/common/protos/api_upstream/openapi/openapiv3.yaml +2734 -708
  37. package/sdk-core/crates/common/protos/api_upstream/temporal/api/activity/v1/message.proto +155 -3
  38. package/sdk-core/crates/common/protos/api_upstream/temporal/api/command/v1/message.proto +26 -0
  39. package/sdk-core/crates/common/protos/api_upstream/temporal/api/common/v1/message.proto +8 -1
  40. package/sdk-core/crates/common/protos/api_upstream/temporal/api/deployment/v1/message.proto +27 -1
  41. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/activity.proto +81 -0
  42. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/event_type.proto +4 -0
  43. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +4 -0
  44. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +15 -0
  45. package/sdk-core/crates/common/protos/api_upstream/temporal/api/enums/v1/workflow.proto +63 -15
  46. package/sdk-core/crates/common/protos/api_upstream/temporal/api/errordetails/v1/message.proto +8 -0
  47. package/sdk-core/crates/common/protos/api_upstream/temporal/api/failure/v1/message.proto +1 -0
  48. package/sdk-core/crates/common/protos/api_upstream/temporal/api/history/v1/message.proto +111 -17
  49. package/sdk-core/crates/common/protos/api_upstream/temporal/api/namespace/v1/message.proto +21 -0
  50. package/sdk-core/crates/common/protos/api_upstream/temporal/api/nexus/v1/message.proto +20 -1
  51. package/sdk-core/crates/common/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +4 -0
  52. package/sdk-core/crates/common/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  53. package/sdk-core/crates/common/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -0
  54. package/sdk-core/crates/common/protos/api_upstream/temporal/api/worker/v1/message.proto +4 -7
  55. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflow/v1/message.proto +80 -22
  56. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +347 -23
  57. package/sdk-core/crates/common/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +242 -43
  58. package/sdk-core/crates/common/protos/local/temporal/sdk/core/core_interface.proto +15 -0
  59. package/sdk-core/crates/common/protos/local/temporal/sdk/core/nexus/nexus.proto +9 -2
  60. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +8 -0
  61. package/sdk-core/crates/common/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +22 -5
  62. package/sdk-core/crates/common/src/activity_definition.rs +20 -0
  63. package/sdk-core/crates/common/src/data_converters.rs +770 -0
  64. package/sdk-core/crates/common/src/envconfig.rs +5 -0
  65. package/sdk-core/crates/common/src/lib.rs +15 -211
  66. package/sdk-core/crates/common/src/payload_visitor.rs +648 -0
  67. package/sdk-core/crates/common/src/priority.rs +110 -0
  68. package/sdk-core/crates/common/src/protos/canned_histories.rs +19 -0
  69. package/sdk-core/crates/common/src/protos/history_builder.rs +45 -0
  70. package/sdk-core/crates/common/src/protos/history_info.rs +2 -0
  71. package/sdk-core/crates/common/src/protos/mod.rs +134 -27
  72. package/sdk-core/crates/common/src/protos/task_token.rs +3 -3
  73. package/sdk-core/crates/common/src/protos/utilities.rs +11 -0
  74. package/sdk-core/crates/{sdk-core → common}/src/telemetry/log_export.rs +11 -16
  75. package/sdk-core/crates/common/src/telemetry/metrics/core.rs +125 -0
  76. package/sdk-core/crates/common/src/telemetry/metrics.rs +272 -225
  77. package/sdk-core/crates/{sdk-core → common}/src/telemetry/otel.rs +8 -13
  78. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_meter.rs +49 -50
  79. package/sdk-core/crates/{sdk-core → common}/src/telemetry/prometheus_server.rs +2 -3
  80. package/sdk-core/crates/common/src/telemetry.rs +278 -19
  81. package/sdk-core/crates/common/src/worker.rs +68 -636
  82. package/sdk-core/crates/common/src/workflow_definition.rs +60 -0
  83. package/sdk-core/crates/macros/Cargo.toml +5 -1
  84. package/sdk-core/crates/macros/src/activities_definitions.rs +585 -0
  85. package/sdk-core/crates/macros/src/fsm_impl.rs +507 -0
  86. package/sdk-core/crates/macros/src/lib.rs +138 -512
  87. package/sdk-core/crates/macros/src/macro_utils.rs +106 -0
  88. package/sdk-core/crates/macros/src/workflow_definitions.rs +1224 -0
  89. package/sdk-core/crates/sdk/Cargo.toml +19 -6
  90. package/sdk-core/crates/sdk/README.md +415 -0
  91. package/sdk-core/crates/sdk/src/activities.rs +417 -0
  92. package/sdk-core/crates/sdk/src/interceptors.rs +1 -1
  93. package/sdk-core/crates/sdk/src/lib.rs +759 -442
  94. package/sdk-core/crates/sdk/src/workflow_context/options.rs +64 -35
  95. package/sdk-core/crates/sdk/src/workflow_context.rs +1033 -289
  96. package/sdk-core/crates/sdk/src/workflow_future.rs +277 -213
  97. package/sdk-core/crates/sdk/src/workflows.rs +711 -0
  98. package/sdk-core/crates/sdk-core/Cargo.toml +59 -65
  99. package/sdk-core/crates/sdk-core/benches/workflow_replay_bench.rs +45 -54
  100. package/sdk-core/crates/sdk-core/machine_coverage/ActivityMachine_Coverage.puml +1 -1
  101. package/sdk-core/crates/sdk-core/src/abstractions.rs +6 -10
  102. package/sdk-core/crates/sdk-core/src/core_tests/activity_tasks.rs +6 -5
  103. package/sdk-core/crates/sdk-core/src/core_tests/mod.rs +22 -21
  104. package/sdk-core/crates/sdk-core/src/core_tests/queries.rs +21 -25
  105. package/sdk-core/crates/sdk-core/src/core_tests/replay_flag.rs +7 -10
  106. package/sdk-core/crates/sdk-core/src/core_tests/updates.rs +14 -17
  107. package/sdk-core/crates/sdk-core/src/core_tests/workers.rs +647 -27
  108. package/sdk-core/crates/sdk-core/src/core_tests/workflow_tasks.rs +46 -41
  109. package/sdk-core/crates/sdk-core/src/ephemeral_server/mod.rs +13 -16
  110. package/sdk-core/crates/sdk-core/src/histfetch.rs +20 -10
  111. package/sdk-core/crates/sdk-core/src/lib.rs +60 -123
  112. package/sdk-core/crates/sdk-core/src/pollers/mod.rs +4 -9
  113. package/sdk-core/crates/sdk-core/src/pollers/poll_buffer.rs +411 -32
  114. package/sdk-core/crates/sdk-core/src/protosext/mod.rs +2 -2
  115. package/sdk-core/crates/sdk-core/src/replay/mod.rs +14 -5
  116. package/sdk-core/crates/sdk-core/src/telemetry/metrics.rs +183 -198
  117. package/sdk-core/crates/sdk-core/src/telemetry/mod.rs +3 -281
  118. package/sdk-core/crates/sdk-core/src/test_help/integ_helpers.rs +35 -16
  119. package/sdk-core/crates/sdk-core/src/test_help/unit_helpers.rs +3 -6
  120. package/sdk-core/crates/sdk-core/src/worker/activities/activity_heartbeat_manager.rs +1 -0
  121. package/sdk-core/crates/sdk-core/src/worker/activities/local_activities.rs +11 -14
  122. package/sdk-core/crates/sdk-core/src/worker/activities.rs +16 -19
  123. package/sdk-core/crates/sdk-core/src/worker/client/mocks.rs +11 -5
  124. package/sdk-core/crates/sdk-core/src/worker/client.rs +104 -86
  125. package/sdk-core/crates/sdk-core/src/worker/heartbeat.rs +10 -14
  126. package/sdk-core/crates/sdk-core/src/worker/mod.rs +1175 -241
  127. package/sdk-core/crates/sdk-core/src/worker/nexus.rs +150 -23
  128. package/sdk-core/crates/sdk-core/src/worker/slot_provider.rs +2 -2
  129. package/sdk-core/crates/sdk-core/src/worker/tuner/fixed_size.rs +2 -2
  130. package/sdk-core/crates/sdk-core/src/worker/tuner/resource_based.rs +25 -27
  131. package/sdk-core/crates/sdk-core/src/worker/tuner.rs +64 -44
  132. package/sdk-core/crates/sdk-core/src/worker/workflow/driven_workflow.rs +9 -3
  133. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/patch_state_machine.rs +5 -8
  134. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +21 -22
  135. package/sdk-core/crates/sdk-core/src/worker/workflow/machines/workflow_machines.rs +28 -4
  136. package/sdk-core/crates/sdk-core/src/worker/workflow/managed_run.rs +20 -41
  137. package/sdk-core/crates/sdk-core/src/worker/workflow/mod.rs +50 -9
  138. package/sdk-core/crates/sdk-core/src/worker/workflow/run_cache.rs +4 -7
  139. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_extraction.rs +2 -4
  140. package/sdk-core/crates/sdk-core/src/worker/workflow/wft_poller.rs +8 -9
  141. package/sdk-core/crates/sdk-core/src/worker/workflow/workflow_stream.rs +1 -3
  142. package/sdk-core/crates/sdk-core/tests/activities_procmacro.rs +6 -0
  143. package/sdk-core/crates/sdk-core/tests/activities_trybuild/basic_pass.rs +54 -0
  144. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.rs +18 -0
  145. package/sdk-core/crates/sdk-core/tests/activities_trybuild/invalid_self_type_fail.stderr +5 -0
  146. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.rs +14 -0
  147. package/sdk-core/crates/sdk-core/tests/activities_trybuild/missing_context_fail.stderr +5 -0
  148. package/sdk-core/crates/sdk-core/tests/activities_trybuild/multi_arg_pass.rs +48 -0
  149. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_input_pass.rs +14 -0
  150. package/sdk-core/crates/sdk-core/tests/activities_trybuild/no_return_type_pass.rs +19 -0
  151. package/sdk-core/crates/sdk-core/tests/cloud_tests.rs +14 -5
  152. package/sdk-core/crates/sdk-core/tests/common/activity_functions.rs +55 -0
  153. package/sdk-core/crates/sdk-core/tests/common/mod.rs +281 -236
  154. package/sdk-core/crates/sdk-core/tests/common/workflows.rs +41 -28
  155. package/sdk-core/crates/sdk-core/tests/global_metric_tests.rs +9 -14
  156. package/sdk-core/crates/sdk-core/tests/heavy_tests/fuzzy_workflow.rs +73 -66
  157. package/sdk-core/crates/sdk-core/tests/heavy_tests.rs +306 -268
  158. package/sdk-core/crates/sdk-core/tests/integ_tests/async_activity_client_tests.rs +230 -0
  159. package/sdk-core/crates/sdk-core/tests/integ_tests/client_tests.rs +94 -57
  160. package/sdk-core/crates/sdk-core/tests/integ_tests/data_converter_tests.rs +381 -0
  161. package/sdk-core/crates/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +37 -38
  162. package/sdk-core/crates/sdk-core/tests/integ_tests/heartbeat_tests.rs +49 -40
  163. package/sdk-core/crates/sdk-core/tests/integ_tests/metrics_tests.rs +447 -300
  164. package/sdk-core/crates/sdk-core/tests/integ_tests/pagination_tests.rs +50 -45
  165. package/sdk-core/crates/sdk-core/tests/integ_tests/polling_tests.rs +157 -157
  166. package/sdk-core/crates/sdk-core/tests/integ_tests/queries_tests.rs +103 -89
  167. package/sdk-core/crates/sdk-core/tests/integ_tests/update_tests.rs +609 -463
  168. package/sdk-core/crates/sdk-core/tests/integ_tests/visibility_tests.rs +80 -62
  169. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_heartbeat_tests.rs +389 -265
  170. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_tests.rs +250 -185
  171. package/sdk-core/crates/sdk-core/tests/integ_tests/worker_versioning_tests.rs +52 -49
  172. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_client_tests.rs +180 -0
  173. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/activities.rs +437 -327
  174. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +82 -58
  175. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +56 -30
  176. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +364 -251
  177. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/client_interactions.rs +552 -0
  178. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +110 -46
  179. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +243 -149
  180. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/eager.rs +98 -32
  181. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1475 -1040
  182. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +73 -43
  183. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +402 -245
  184. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/patches.rs +343 -207
  185. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/queries.rs +415 -0
  186. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/replay.rs +96 -36
  187. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/resets.rs +155 -140
  188. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/signals.rs +183 -113
  189. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +85 -44
  190. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/timers.rs +142 -48
  191. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +73 -56
  192. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests.rs +365 -242
  193. package/sdk-core/crates/sdk-core/tests/main.rs +22 -16
  194. package/sdk-core/crates/sdk-core/tests/manual_tests.rs +233 -187
  195. package/sdk-core/crates/sdk-core/tests/runner.rs +4 -6
  196. package/sdk-core/crates/sdk-core/tests/shared_tests/mod.rs +73 -27
  197. package/sdk-core/crates/sdk-core/tests/shared_tests/priority.rs +107 -84
  198. package/sdk-core/crates/sdk-core/tests/workflows_procmacro.rs +6 -0
  199. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.rs +26 -0
  200. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/async_query_fail.stderr +5 -0
  201. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/basic_pass.rs +49 -0
  202. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/minimal_pass.rs +21 -0
  203. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.rs +26 -0
  204. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/mut_query_fail.stderr +5 -0
  205. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.rs +21 -0
  206. package/sdk-core/crates/sdk-core/tests/workflows_trybuild/sync_run_fail.stderr +5 -0
  207. package/sdk-core/crates/sdk-core-c-bridge/Cargo.toml +8 -1
  208. package/sdk-core/crates/sdk-core-c-bridge/include/temporal-sdk-core-c-bridge.h +37 -26
  209. package/sdk-core/crates/sdk-core-c-bridge/src/client.rs +180 -87
  210. package/sdk-core/crates/sdk-core-c-bridge/src/lib.rs +89 -5
  211. package/sdk-core/crates/sdk-core-c-bridge/src/metric.rs +10 -16
  212. package/sdk-core/crates/sdk-core-c-bridge/src/runtime.rs +59 -67
  213. package/sdk-core/crates/sdk-core-c-bridge/src/testing.rs +10 -10
  214. package/sdk-core/crates/sdk-core-c-bridge/src/tests/context.rs +57 -22
  215. package/sdk-core/crates/sdk-core-c-bridge/src/tests/mod.rs +108 -12
  216. package/sdk-core/crates/sdk-core-c-bridge/src/tests/utils.rs +9 -52
  217. package/sdk-core/crates/sdk-core-c-bridge/src/worker.rs +74 -91
  218. package/sdk-core/rustfmt.toml +2 -1
  219. package/src/client.rs +206 -289
  220. package/src/helpers/try_into_js.rs +88 -2
  221. package/src/metrics.rs +277 -35
  222. package/src/runtime.rs +94 -45
  223. package/src/testing.rs +9 -16
  224. package/src/worker.rs +86 -68
  225. package/ts/native.ts +39 -3
  226. package/sdk-core/crates/client/src/workflow_handle/mod.rs +0 -212
  227. package/sdk-core/crates/common/src/errors.rs +0 -85
  228. package/sdk-core/crates/common/tests/worker_task_types_test.rs +0 -129
  229. package/sdk-core/crates/macros/LICENSE.txt +0 -21
  230. package/sdk-core/crates/sdk/src/activity_context.rs +0 -238
  231. package/sdk-core/crates/sdk/src/app_data.rs +0 -37
  232. package/sdk-core/crates/sdk-core/tests/integ_tests/activity_functions.rs +0 -5
  233. package/sdk-core/crates/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +0 -61
@@ -2,10 +2,12 @@ use crate::{
2
2
  abstractions::{ActiveCounter, MeteredPermitDealer, OwnedMeteredSemPermit, dbg_panic},
3
3
  pollers::{self, Poller},
4
4
  worker::{
5
- WFTPollerShared,
5
+ ActivitySlotKind, NexusSlotKind, PollerBehavior, SlotKind, WFTPollerShared,
6
+ WorkflowSlotKind,
6
7
  client::{PollActivityOptions, PollOptions, PollWorkflowOptions, WorkerClient},
7
8
  },
8
9
  };
10
+ use backoff::{SystemClock, backoff::Backoff, exponential::ExponentialBackoff};
9
11
  use crossbeam_utils::atomic::AtomicCell;
10
12
  use futures_util::{FutureExt, StreamExt, future::BoxFuture};
11
13
  use governor::{Quota, RateLimiter};
@@ -22,15 +24,11 @@ use std::{
22
24
  use temporalio_client::{
23
25
  ERROR_RETURNED_DUE_TO_SHORT_CIRCUIT, request_extensions::NoRetryOnMatching,
24
26
  };
25
- use temporalio_common::{
26
- protos::temporal::api::{
27
- taskqueue::v1::PollerScalingDecision,
28
- workflowservice::v1::{
29
- PollActivityTaskQueueResponse, PollNexusTaskQueueResponse,
30
- PollWorkflowTaskQueueResponse,
31
- },
27
+ use temporalio_common::protos::temporal::api::{
28
+ taskqueue::v1::PollerScalingDecision,
29
+ workflowservice::v1::{
30
+ PollActivityTaskQueueResponse, PollNexusTaskQueueResponse, PollWorkflowTaskQueueResponse,
32
31
  },
33
- worker::{ActivitySlotKind, NexusSlotKind, PollerBehavior, SlotKind, WorkflowSlotKind},
34
32
  };
35
33
  use tokio::{
36
34
  sync::{
@@ -79,6 +77,7 @@ impl LongPollBuffer<PollWorkflowTaskQueueResponse, WorkflowSlotKind> {
79
77
  num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
80
78
  options: WorkflowTaskOptions,
81
79
  last_successful_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
80
+ graceful_poll_shutdown: Arc<AtomicBool>,
82
81
  ) -> Self {
83
82
  let is_sticky = sticky_queue.is_some();
84
83
  let poll_scaler = PollScaler::new(
@@ -141,6 +140,7 @@ impl LongPollBuffer<PollWorkflowTaskQueueResponse, WorkflowSlotKind> {
141
140
  poll_scaler,
142
141
  pre_permit_delay,
143
142
  post_poll_fn,
143
+ graceful_poll_shutdown,
144
144
  )
145
145
  }
146
146
  }
@@ -156,6 +156,7 @@ impl LongPollBuffer<PollActivityTaskQueueResponse, ActivitySlotKind> {
156
156
  num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
157
157
  options: ActivityTaskOptions,
158
158
  last_successful_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
159
+ graceful_poll_shutdown: Arc<AtomicBool>,
159
160
  ) -> Self {
160
161
  let pre_permit_delay = options
161
162
  .max_worker_acts_per_second
@@ -208,6 +209,7 @@ impl LongPollBuffer<PollActivityTaskQueueResponse, ActivitySlotKind> {
208
209
  poll_scaler,
209
210
  pre_permit_delay,
210
211
  None::<fn(&PollActivityTaskQueueResponse)>,
212
+ graceful_poll_shutdown,
211
213
  )
212
214
  }
213
215
  }
@@ -223,6 +225,7 @@ impl LongPollBuffer<PollNexusTaskQueueResponse, NexusSlotKind> {
223
225
  num_pollers_handler: Option<impl Fn(usize) + Send + Sync + 'static>,
224
226
  last_successful_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
225
227
  send_heartbeat: bool,
228
+ graceful_poll_shutdown: Arc<AtomicBool>,
226
229
  ) -> Self {
227
230
  let no_retry = if matches!(poller_behavior, PollerBehavior::Autoscaling { .. }) {
228
231
  Some(NoRetryOnMatching {
@@ -259,10 +262,20 @@ impl LongPollBuffer<PollNexusTaskQueueResponse, NexusSlotKind> {
259
262
  ),
260
263
  None::<fn() -> BoxFuture<'static, ()>>,
261
264
  None::<fn(&PollNexusTaskQueueResponse)>,
265
+ graceful_poll_shutdown,
262
266
  )
263
267
  }
264
268
  }
265
269
 
270
+ // Simple way to test this w/o plumbing options through. This will be removed after
271
+ // the proper server implementation for terminating polls on worker shutdown exists.
272
+ #[cfg(test)]
273
+ use std::cell::RefCell;
274
+ #[cfg(test)]
275
+ thread_local! {
276
+ static POLL_SHUTDOWN_INTERRUPT: RefCell<Option<Duration>> = RefCell::default();
277
+ }
278
+
266
279
  impl<T, SK> LongPollBuffer<T, SK>
267
280
  where
268
281
  T: TaskPollerResult + Send + Debug + 'static,
@@ -275,6 +288,7 @@ where
275
288
  mut poll_scaler: PollScaler<F>,
276
289
  pre_permit_delay: Option<impl Fn() -> DelayFut + Send + Sync + 'static>,
277
290
  post_poll_fn: Option<impl Fn(&T) + Send + Sync + 'static>,
291
+ graceful_shutdown: Arc<AtomicBool>,
278
292
  ) -> Self
279
293
  where
280
294
  FT: Future<Output = pollers::Result<T>> + Send,
@@ -282,11 +296,18 @@ where
282
296
  F: Fn(usize) + Send + Sync + 'static,
283
297
  {
284
298
  let (tx, rx) = unbounded_channel();
285
- let (starter, wait_for_start) = broadcast::channel(1);
299
+ let (starter, mut wait_for_start) = broadcast::channel(1);
286
300
  let pf = Arc::new(poll_fn);
287
301
  let post_pf = Arc::new(post_poll_fn);
288
- let mut wait_for_start = wait_for_start.resubscribe();
289
302
  let shutdown_clone = shutdown.clone();
303
+ #[cfg(test)]
304
+ let poll_shutdown_interrupt_wait = POLL_SHUTDOWN_INTERRUPT.with(|v| *v.borrow());
305
+ #[cfg(not(test))]
306
+ let poll_shutdown_interrupt_wait =
307
+ std::env::var("TEMPORAL_POLL_SHUTDOWN_INTERRUPT_WAIT_MS")
308
+ .ok()
309
+ .and_then(|s| s.parse::<u64>().ok())
310
+ .map(Duration::from_millis);
290
311
 
291
312
  let poller_task = tokio::spawn(
292
313
  async move {
@@ -338,18 +359,38 @@ where
338
359
  } else {
339
360
  None
340
361
  };
362
+ let graceful_shutdown = graceful_shutdown.clone();
341
363
  let poll_task = tokio::spawn(async move {
342
- let r = tokio::select! {
343
- r = pf(timeout_override) => r,
344
- _ = shutdown.cancelled() => return,
364
+ let shutdown_clone = shutdown.clone();
365
+
366
+ let r = if graceful_shutdown.load(Ordering::Relaxed) {
367
+ pf(timeout_override).await
368
+ } else {
369
+ let poll_interruptor = shutdown.cancelled().then(|_| async move {
370
+ if let Some(w) = poll_shutdown_interrupt_wait {
371
+ tokio::time::sleep(w).await;
372
+ }
373
+ });
374
+ tokio::select! {
375
+ r = pf(timeout_override) => r,
376
+ _ = poll_interruptor => return,
377
+ }
345
378
  };
346
- drop(active_guard);
347
379
  if let Ok(r) = &r
348
380
  && let Some(ppf) = post_pf.as_ref()
349
381
  {
350
382
  ppf(r);
351
383
  }
352
- if report_handle.poll_result(&r) {
384
+ let (should_forward, backoff_duration) = report_handle.poll_result(&r);
385
+ if let Some(duration) = backoff_duration {
386
+ // Apply backoff BEFORE dropping active_guard to prevent next poll from starting
387
+ tokio::select! {
388
+ _ = tokio::time::sleep(duration) => return,
389
+ _ = shutdown_clone.cancelled() => (),
390
+ };
391
+ }
392
+ drop(active_guard);
393
+ if should_forward {
353
394
  let _ = tx.send(r.map(|r| (r, permit)));
354
395
  }
355
396
  });
@@ -463,6 +504,28 @@ where
463
504
  ingested_last_period: Default::default(),
464
505
  scale_up_allowed: AtomicBool::new(true),
465
506
  last_successful_poll_time,
507
+ exponential_backoff: parking_lot::Mutex::new(ExponentialBackoff {
508
+ // Copied from RetryOptions::task_poll_retry_policy()
509
+ current_interval: Duration::from_millis(200),
510
+ initial_interval: Duration::from_millis(200),
511
+ randomization_factor: 0.2,
512
+ multiplier: 2.0,
513
+ max_interval: Duration::from_secs(10),
514
+ max_elapsed_time: None,
515
+ clock: SystemClock::default(),
516
+ start_time: std::time::Instant::now(),
517
+ }),
518
+ resource_exhausted_backoff: parking_lot::Mutex::new(ExponentialBackoff {
519
+ // Copied from RetryOptions::throttle_retry_policy()
520
+ current_interval: Duration::from_secs(1),
521
+ initial_interval: Duration::from_secs(1),
522
+ randomization_factor: 0.2,
523
+ multiplier: 2.0,
524
+ max_interval: Duration::from_secs(10),
525
+ max_elapsed_time: None,
526
+ clock: SystemClock::default(),
527
+ start_time: std::time::Instant::now(),
528
+ }),
466
529
  });
467
530
  let rhc = report_handle.clone();
468
531
  let ingestor_task = if behavior.is_autoscaling() {
@@ -526,18 +589,32 @@ struct PollScalerReportHandle {
526
589
  ingested_last_period: AtomicUsize,
527
590
  scale_up_allowed: AtomicBool,
528
591
  last_successful_poll_time: Arc<AtomicCell<Option<SystemTime>>>,
592
+
593
+ // Exponential backoff for normal errors and resource exhausted errors
594
+ exponential_backoff: parking_lot::Mutex<ExponentialBackoff<SystemClock>>,
595
+ resource_exhausted_backoff: parking_lot::Mutex<ExponentialBackoff<SystemClock>>,
529
596
  }
530
597
 
531
598
  impl PollScalerReportHandle {
532
- /// Returns true if the response should be passed on, false if it should be swallowed
533
- fn poll_result(&self, res: &Result<impl TaskPollerResult, tonic::Status>) -> bool {
599
+ /// Returns (should_forward, backoff_duration)
600
+ /// - should_forward: true if the response should be passed on, false if it should be swallowed
601
+ /// - backoff_duration: Some(duration) if we should sleep before the next poll
602
+ fn poll_result(
603
+ &self,
604
+ res: &Result<impl TaskPollerResult, tonic::Status>,
605
+ ) -> (bool, Option<Duration>) {
534
606
  match res {
535
607
  Ok(res) => {
536
608
  self.last_successful_poll_time
537
609
  .store(Some(SystemTime::now()));
610
+
611
+ // Reset backoff on successful poll
612
+ self.exponential_backoff.lock().reset();
613
+ self.resource_exhausted_backoff.lock().reset();
614
+
538
615
  if let PollerBehavior::SimpleMaximum(_) = self.behavior {
539
616
  // We don't do auto-scaling with the simple max
540
- return true;
617
+ return (true, None);
541
618
  }
542
619
  if !res.is_empty() {
543
620
  self.ingested_this_period.fetch_add(1, Ordering::Relaxed);
@@ -572,30 +649,36 @@ impl PollScalerReportHandle {
572
649
  }
573
650
  Err(e) => {
574
651
  if matches!(self.behavior, PollerBehavior::Autoscaling { .. }) {
575
- // We should only react to errors in autoscaling mode if we saw a scaling
576
- // decision
652
+ // Follow the same backoff logic as the retry client
653
+ let mut backoff_duration = self.exponential_backoff.lock().next_backoff();
654
+ if e.code() == Code::ResourceExhausted {
655
+ backoff_duration = self.resource_exhausted_backoff.lock().next_backoff();
656
+ };
657
+
658
+ // Only propagate errors out if they weren't because of the short-circuiting
659
+ // logic. IE: We don't want to fail callers because we said we wanted to know
660
+ // about ResourceExhausted errors, but we haven't seen a scaling decision yet,
661
+ // so we're not reacting to errors, only propagating them.
662
+ let should_forward = !e
663
+ .metadata()
664
+ .contains_key(ERROR_RETURNED_DUE_TO_SHORT_CIRCUIT);
665
+
666
+ // We should only react to errors in autoscaling mode if we saw a scaling decision
577
667
  if self.ever_saw_scaling_decision.load(Ordering::Relaxed) {
578
668
  debug!("Got error from server while polling: {:?}", e);
579
669
  if e.code() == Code::ResourceExhausted {
580
670
  // Scale down significantly for resource exhaustion
581
671
  self.change_target(usize::saturating_div, 2);
582
672
  } else {
583
- // Other codes that would normally have made us back off briefly can
584
- // reclaim this poller
673
+ // Other codes that would normally have made us back off briefly can reclaim this poller
585
674
  self.change_target(usize::saturating_sub, 1);
586
675
  }
587
676
  }
588
- // Only propagate errors out if they weren't because of the short-circuiting
589
- // logic. IE: We don't want to fail callers because we said we wanted to know
590
- // about ResourceExhausted errors, but we haven't seen a scaling decision yet,
591
- // so we're not reacting to errors, only propagating them.
592
- return !e
593
- .metadata()
594
- .contains_key(ERROR_RETURNED_DUE_TO_SHORT_CIRCUIT);
677
+ return (should_forward, backoff_duration);
595
678
  }
596
679
  }
597
680
  }
598
- true
681
+ (true, None)
599
682
  }
600
683
 
601
684
  #[inline]
@@ -740,8 +823,9 @@ mod tests {
740
823
  worker::client::mocks::mock_manual_worker_client,
741
824
  };
742
825
  use futures_util::FutureExt;
826
+ use rstest::rstest;
743
827
  use std::time::Duration;
744
- use tokio::select;
828
+ use tokio::{select, sync::Notify};
745
829
 
746
830
  #[tokio::test]
747
831
  async fn only_polls_once_with_1_poller() {
@@ -769,6 +853,7 @@ mod tests {
769
853
  wft_poller_shared: Some(Arc::new(WFTPollerShared::new(Some(10)))),
770
854
  },
771
855
  Arc::new(AtomicCell::new(None)),
856
+ Arc::new(AtomicBool::new(false)),
772
857
  );
773
858
 
774
859
  // Poll a bunch of times, "interrupting" it each time, we should only actually have polled
@@ -825,10 +910,304 @@ mod tests {
825
910
  wft_poller_shared: Some(Arc::new(WFTPollerShared::new(Some(1)))),
826
911
  },
827
912
  Arc::new(AtomicCell::new(None)),
913
+ Arc::new(AtomicBool::new(false)),
828
914
  );
829
915
 
830
916
  // Should not see error, unwraps should get empty response
831
917
  pb.poll().await.unwrap().unwrap();
832
918
  pb.shutdown().await;
833
919
  }
920
+
921
+ #[tokio::test]
922
+ async fn poll_shutdown_waits_interrupt_period_before_cancelling() {
923
+ POLL_SHUTDOWN_INTERRUPT.with(|v| {
924
+ *v.borrow_mut() = Some(Duration::from_millis(200));
925
+ });
926
+
927
+ let mut mock_client = mock_manual_worker_client();
928
+ let call_count = Arc::new(AtomicUsize::new(0));
929
+ let call_count_clone = call_count.clone();
930
+
931
+ let second_task_complete = Arc::new(Notify::new());
932
+ let second_task_complete_clone = second_task_complete.clone();
933
+ let third_task_complete = Arc::new(Notify::new());
934
+ let third_task_complete_clone = third_task_complete.clone();
935
+
936
+ let second_started = Arc::new(Notify::new());
937
+ let second_started_clone = second_started.clone();
938
+ let third_started = Arc::new(Notify::new());
939
+ let third_started_clone = third_started.clone();
940
+
941
+ mock_client
942
+ .expect_poll_workflow_task()
943
+ .returning(move |_, _| {
944
+ let count = call_count_clone.fetch_add(1, Ordering::SeqCst);
945
+ let second_complete = second_task_complete_clone.clone();
946
+ let third_complete = third_task_complete_clone.clone();
947
+ let second_started = second_started_clone.clone();
948
+ let third_started = third_started_clone.clone();
949
+
950
+ async move {
951
+ match count {
952
+ 0 => Ok(PollWorkflowTaskQueueResponse {
953
+ task_token: vec![1],
954
+ ..Default::default()
955
+ }),
956
+ 1 => {
957
+ second_started.notify_one();
958
+ second_complete.notified().await;
959
+ Ok(PollWorkflowTaskQueueResponse {
960
+ task_token: vec![2],
961
+ ..Default::default()
962
+ })
963
+ }
964
+ _ => {
965
+ third_started.notify_one();
966
+ third_complete.notified().await;
967
+ Ok(PollWorkflowTaskQueueResponse {
968
+ task_token: vec![3],
969
+ ..Default::default()
970
+ })
971
+ }
972
+ }
973
+ }
974
+ .boxed()
975
+ });
976
+
977
+ let shutdown_token = CancellationToken::new();
978
+ let pb = LongPollBuffer::new_workflow_task(
979
+ Arc::new(mock_client),
980
+ "sometq".to_string(),
981
+ None,
982
+ PollerBehavior::SimpleMaximum(3),
983
+ fixed_size_permit_dealer(10),
984
+ shutdown_token.clone(),
985
+ None::<fn(usize)>,
986
+ WorkflowTaskOptions {
987
+ wft_poller_shared: Some(Arc::new(WFTPollerShared::new(Some(10)))),
988
+ },
989
+ Arc::new(AtomicCell::new(None)),
990
+ Arc::new(AtomicBool::new(false)),
991
+ );
992
+
993
+ let first_task = pb.poll().await.expect("Should get first task");
994
+ assert!(first_task.is_ok());
995
+ assert_eq!(first_task.unwrap().0.task_token, vec![1]);
996
+
997
+ // Wait for both second and third polls to start
998
+ second_started.notified().await;
999
+ third_started.notified().await;
1000
+
1001
+ // Now both polls 2 and 3 are in progress. Trigger shutdown.
1002
+ let shutdown_time = std::time::Instant::now();
1003
+ shutdown_token.cancel();
1004
+
1005
+ // Immediately unlock the second task so it completes quickly (within interrupt wait)
1006
+ second_task_complete.notify_one();
1007
+
1008
+ // Second task should be received because it completes within the interrupt wait period
1009
+ let (task, _) = pb.poll().await.unwrap().unwrap();
1010
+ assert_eq!(task.task_token, vec![2]);
1011
+
1012
+ // Don't notify third_task_complete - the third poll will wait for the interrupt period
1013
+ // and then be interrupted. Should return None.
1014
+ let third_task_result = pb.poll().await;
1015
+ let elapsed = shutdown_time.elapsed();
1016
+
1017
+ assert!(
1018
+ third_task_result.is_none(),
1019
+ "Third task should not be received - poll should be interrupted after interrupt period"
1020
+ );
1021
+
1022
+ // Total elapsed time should be at least the interrupt wait (200ms), but not significantly
1023
+ // longer.
1024
+ assert!(
1025
+ elapsed >= Duration::from_millis(200),
1026
+ "Should wait at least the interrupt period. Elapsed: {elapsed:?}",
1027
+ );
1028
+ assert!(
1029
+ elapsed < Duration::from_secs(1),
1030
+ "Should not wait too long. Elapsed: {elapsed:?}",
1031
+ );
1032
+
1033
+ // Clean up
1034
+ POLL_SHUTDOWN_INTERRUPT.with(|v| {
1035
+ *v.borrow_mut() = None;
1036
+ });
1037
+ }
1038
+
1039
+ #[rstest]
1040
+ #[case::resource_exhausted(Code::ResourceExhausted)]
1041
+ #[case::internal(Code::Internal)]
1042
+ #[tokio::test]
1043
+ async fn autoscaler_applies_backoff_on_errors(#[case] error_code: Code) {
1044
+ use temporalio_common::protos::temporal::api::taskqueue::v1::PollerScalingDecision;
1045
+
1046
+ let call_count = Arc::new(AtomicUsize::new(0));
1047
+ let call_count_clone = call_count.clone();
1048
+ let first_poll_done = Arc::new(AtomicBool::new(false));
1049
+ let first_poll_done_clone = first_poll_done.clone();
1050
+
1051
+ let mut mock_client = mock_manual_worker_client();
1052
+ mock_client
1053
+ .expect_poll_workflow_task()
1054
+ .returning(move |_, _| {
1055
+ call_count_clone.fetch_add(1, Ordering::SeqCst);
1056
+ let first_done = first_poll_done_clone.clone();
1057
+ async move {
1058
+ // First poll: return empty response with scaling decision
1059
+ // This sets ever_saw_scaling_decision to true
1060
+ if !first_done.swap(true, Ordering::SeqCst) {
1061
+ Ok(PollWorkflowTaskQueueResponse {
1062
+ task_token: vec![], // Empty poll
1063
+ poller_scaling_decision: Some(PollerScalingDecision {
1064
+ // Aggressively scale up to max
1065
+ poll_request_delta_suggestion: 0,
1066
+ }),
1067
+ ..Default::default()
1068
+ })
1069
+ } else {
1070
+ // All subsequent polls fail with a grpc error
1071
+ Err(tonic::Status::new(
1072
+ error_code,
1073
+ format!("simulated grpc error {error_code}"),
1074
+ ))
1075
+ }
1076
+ }
1077
+ .boxed()
1078
+ });
1079
+
1080
+ let pb = Arc::new(LongPollBuffer::new_workflow_task(
1081
+ Arc::new(mock_client),
1082
+ "sometq".to_string(),
1083
+ None,
1084
+ PollerBehavior::Autoscaling {
1085
+ minimum: 5,
1086
+ maximum: 100,
1087
+ initial: 10,
1088
+ },
1089
+ fixed_size_permit_dealer(10),
1090
+ CancellationToken::new(),
1091
+ None::<fn(usize)>,
1092
+ WorkflowTaskOptions {
1093
+ wft_poller_shared: Some(Arc::new(WFTPollerShared::new(Some(10)))),
1094
+ },
1095
+ Arc::new(AtomicCell::new(None)),
1096
+ Arc::new(AtomicBool::new(false)),
1097
+ ));
1098
+
1099
+ // Trigger the first poll to initialize and get the scaling decision
1100
+ let pb_clone = pb.clone();
1101
+ tokio::spawn(async move {
1102
+ let _ = pb_clone.poll().await;
1103
+ });
1104
+
1105
+ // Wait for the first poll to complete
1106
+ tokio::time::sleep(Duration::from_millis(20)).await;
1107
+
1108
+ // Let the hot loop run for 100ms while we continue to attempt consuming
1109
+ tokio::time::sleep(Duration::from_millis(100)).await;
1110
+ let hot_loop_calls = call_count.load(Ordering::SeqCst);
1111
+
1112
+ // Without backoff, this was producing ~6300 polls in ~100ms on my machine.
1113
+ // With exponential backoff, I'm getting exactly 10 (initial poller count).
1114
+ assert!(
1115
+ hot_loop_calls == 10,
1116
+ "Expected proper backoff with == 10 polls in 100ms, but got {} polls.",
1117
+ hot_loop_calls
1118
+ );
1119
+
1120
+ Arc::try_unwrap(pb)
1121
+ .unwrap_or_else(|_| panic!("Failed to unwrap Arc"))
1122
+ .shutdown()
1123
+ .await;
1124
+ }
1125
+
1126
+ #[rstest]
1127
+ #[case::graceful(true)]
1128
+ #[case::legacy(false)]
1129
+ #[tokio::test]
1130
+ async fn inflight_poll_survives_shutdown_only_when_graceful(#[case] graceful: bool) {
1131
+ let mut mock_client = mock_manual_worker_client();
1132
+ let task_started = Arc::new(Notify::new());
1133
+ let task_started_clone = task_started.clone();
1134
+ let task_complete = Arc::new(Notify::new());
1135
+ let task_complete_clone = task_complete.clone();
1136
+ let call_count = Arc::new(AtomicUsize::new(0));
1137
+ let call_count_clone = call_count.clone();
1138
+
1139
+ mock_client
1140
+ .expect_poll_workflow_task()
1141
+ .returning(move |_, _| {
1142
+ let count = call_count_clone.fetch_add(1, Ordering::SeqCst);
1143
+ let started = task_started_clone.clone();
1144
+ let complete = task_complete_clone.clone();
1145
+ async move {
1146
+ match count {
1147
+ 0 => Ok(PollWorkflowTaskQueueResponse {
1148
+ task_token: vec![1],
1149
+ ..Default::default()
1150
+ }),
1151
+ _ => {
1152
+ started.notify_one();
1153
+ complete.notified().await;
1154
+ Ok(PollWorkflowTaskQueueResponse {
1155
+ task_token: vec![2],
1156
+ ..Default::default()
1157
+ })
1158
+ }
1159
+ }
1160
+ }
1161
+ .boxed()
1162
+ });
1163
+
1164
+ let shutdown_token = CancellationToken::new();
1165
+ let pb = LongPollBuffer::new_workflow_task(
1166
+ Arc::new(mock_client),
1167
+ "sometq".to_string(),
1168
+ None,
1169
+ PollerBehavior::SimpleMaximum(1),
1170
+ fixed_size_permit_dealer(10),
1171
+ shutdown_token.clone(),
1172
+ None::<fn(usize)>,
1173
+ WorkflowTaskOptions {
1174
+ wft_poller_shared: None,
1175
+ },
1176
+ Arc::new(AtomicCell::new(None)),
1177
+ Arc::new(AtomicBool::new(graceful)),
1178
+ );
1179
+
1180
+ let first = pb.poll().await.unwrap().unwrap();
1181
+ assert_eq!(first.0.task_token, vec![1]);
1182
+
1183
+ // Wait for second poll to be in-flight
1184
+ task_started.notified().await;
1185
+
1186
+ shutdown_token.cancel();
1187
+
1188
+ if graceful {
1189
+ // Release the poll — simulates server returning empty after ShutdownWorker
1190
+ task_complete.notify_one();
1191
+
1192
+ // Graceful: in-flight poll survives, second task is received
1193
+ let second = tokio::time::timeout(Duration::from_secs(2), pb.poll())
1194
+ .await
1195
+ .expect("graceful poll should complete")
1196
+ .unwrap()
1197
+ .unwrap();
1198
+ assert_eq!(second.0.task_token, vec![2]);
1199
+ } else {
1200
+ // Don't release — the poll should be killed by shutdown token before completing.
1201
+ // Buffer drains to None because the killed poll sends no result.
1202
+ let result = tokio::time::timeout(Duration::from_secs(2), pb.poll())
1203
+ .await
1204
+ .expect("legacy poll should resolve quickly");
1205
+ assert!(
1206
+ result.is_none(),
1207
+ "Legacy shutdown should kill in-flight poll, buffer returns None"
1208
+ );
1209
+ }
1210
+
1211
+ pb.shutdown().await;
1212
+ }
834
1213
  }
@@ -1,10 +1,10 @@
1
1
  pub(crate) mod protocol_messages;
2
2
 
3
3
  use crate::{
4
- CompleteActivityError, TaskToken,
4
+ TaskToken,
5
5
  protosext::protocol_messages::IncomingProtocolMessage,
6
6
  retry_logic::ValidatedRetryPolicy,
7
- worker::{LEGACY_QUERY_ID, LocalActivityExecutionResult},
7
+ worker::{CompleteActivityError, LEGACY_QUERY_ID, LocalActivityExecutionResult},
8
8
  };
9
9
  use anyhow::anyhow;
10
10
  use itertools::Itertools;
@@ -3,9 +3,9 @@
3
3
  //! users during testing.
4
4
 
5
5
  use crate::{
6
- Worker,
6
+ Worker, WorkerConfig,
7
7
  worker::{
8
- PostActivateHookData,
8
+ PollerBehavior, PostActivateHookData,
9
9
  client::mocks::{MockManualWorkerClient, mock_manual_worker_client},
10
10
  },
11
11
  };
@@ -30,7 +30,7 @@ use temporalio_common::{
30
30
  },
31
31
  },
32
32
  },
33
- worker::{PollerBehavior, WorkerConfig, WorkerTaskTypes},
33
+ worker::WorkerTaskTypes,
34
34
  };
35
35
  use tokio::sync::{Mutex as TokioMutex, mpsc, mpsc::UnboundedSender};
36
36
  use tokio_stream::wrappers::UnboundedReceiverStream;
@@ -126,11 +126,20 @@ where
126
126
 
127
127
  /// A history which will be used during replay verification. Since histories do not include the
128
128
  /// workflow id, it must be manually attached.
129
- #[derive(Debug, Clone, derive_more::Constructor)]
129
+ #[derive(Debug, Clone)]
130
130
  pub struct HistoryForReplay {
131
131
  hist: History,
132
132
  workflow_id: String,
133
133
  }
134
+ impl HistoryForReplay {
135
+ /// Create a new history from replay from something that looks like a history and a workflow id.
136
+ pub fn new(history: impl Into<History>, workflow_id: impl Into<String>) -> Self {
137
+ Self {
138
+ hist: history.into(),
139
+ workflow_id: workflow_id.into(),
140
+ }
141
+ }
142
+ }
134
143
  impl From<TestHistoryBuilder> for HistoryForReplay {
135
144
  fn from(thb: TestHistoryBuilder) -> Self {
136
145
  thb.get_full_history_info().unwrap().into()
@@ -138,7 +147,7 @@ impl From<TestHistoryBuilder> for HistoryForReplay {
138
147
  }
139
148
  impl From<HistoryInfo> for HistoryForReplay {
140
149
  fn from(histinfo: HistoryInfo) -> Self {
141
- HistoryForReplay::new(histinfo.into(), "fake".to_owned())
150
+ HistoryForReplay::new(histinfo, "fake".to_owned())
142
151
  }
143
152
  }
144
153