temporalio 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -23
  3. data/bridge/Cargo.lock +168 -59
  4. data/bridge/Cargo.toml +4 -2
  5. data/bridge/sdk-core/README.md +19 -6
  6. data/bridge/sdk-core/client/src/lib.rs +215 -39
  7. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  8. data/bridge/sdk-core/client/src/raw.rs +4 -4
  9. data/bridge/sdk-core/client/src/retry.rs +32 -20
  10. data/bridge/sdk-core/core/Cargo.toml +22 -9
  11. data/bridge/sdk-core/core/src/abstractions.rs +203 -14
  12. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +76 -41
  13. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  14. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +204 -83
  15. data/bridge/sdk-core/core/src/core_tests/queries.rs +3 -4
  16. data/bridge/sdk-core/core/src/core_tests/workers.rs +1 -3
  17. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +397 -54
  18. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  19. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  20. data/bridge/sdk-core/core/src/lib.rs +16 -9
  21. data/bridge/sdk-core/core/src/telemetry/log_export.rs +1 -1
  22. data/bridge/sdk-core/core/src/telemetry/metrics.rs +69 -35
  23. data/bridge/sdk-core/core/src/telemetry/mod.rs +29 -13
  24. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +17 -12
  25. data/bridge/sdk-core/core/src/test_help/mod.rs +62 -12
  26. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  27. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  28. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +352 -122
  29. data/bridge/sdk-core/core/src/worker/activities.rs +233 -157
  30. data/bridge/sdk-core/core/src/worker/client/mocks.rs +22 -2
  31. data/bridge/sdk-core/core/src/worker/client.rs +18 -2
  32. data/bridge/sdk-core/core/src/worker/mod.rs +165 -58
  33. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  34. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  35. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +856 -277
  36. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +100 -43
  37. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +7 -7
  38. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +5 -4
  39. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +87 -27
  40. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +5 -4
  41. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +5 -4
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +5 -4
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +137 -62
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +25 -17
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +7 -6
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +103 -152
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +7 -7
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -7
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +5 -16
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +201 -121
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +11 -14
  54. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +30 -15
  55. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1026 -376
  56. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +460 -384
  57. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  58. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  60. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +448 -718
  63. data/bridge/sdk-core/core-api/Cargo.toml +2 -1
  64. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  65. data/bridge/sdk-core/core-api/src/lib.rs +6 -2
  66. data/bridge/sdk-core/core-api/src/telemetry.rs +0 -6
  67. data/bridge/sdk-core/core-api/src/worker.rs +14 -1
  68. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  69. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  70. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  71. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
  72. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
  73. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
  74. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
  75. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
  76. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
  77. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
  78. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
  79. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
  80. data/bridge/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
  81. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  82. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
  83. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
  84. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  85. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  86. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  87. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  88. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  89. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  90. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  91. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +7 -0
  92. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -0
  93. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  94. data/bridge/sdk-core/sdk/Cargo.toml +3 -2
  95. data/bridge/sdk-core/sdk/src/lib.rs +87 -20
  96. data/bridge/sdk-core/sdk/src/workflow_future.rs +9 -8
  97. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  98. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -1
  99. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +100 -87
  100. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +5 -1
  101. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +175 -57
  102. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  103. data/bridge/sdk-core/test-utils/Cargo.toml +3 -1
  104. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  105. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  106. data/bridge/sdk-core/test-utils/src/lib.rs +82 -23
  107. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  108. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  109. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  110. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  111. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  112. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
  113. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  114. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  115. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  116. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  117. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  118. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  119. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  120. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  121. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  122. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  123. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +72 -191
  124. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -0
  125. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  126. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
  127. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  128. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  129. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  130. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  131. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  132. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +10 -11
  133. data/bridge/sdk-core/tests/main.rs +3 -13
  134. data/bridge/sdk-core/tests/runner.rs +75 -36
  135. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  136. data/bridge/src/connection.rs +41 -25
  137. data/bridge/src/lib.rs +269 -14
  138. data/bridge/src/runtime.rs +1 -1
  139. data/bridge/src/test_server.rs +153 -0
  140. data/bridge/src/worker.rs +89 -16
  141. data/lib/gen/temporal/api/command/v1/message_pb.rb +4 -18
  142. data/lib/gen/temporal/api/common/v1/message_pb.rb +4 -0
  143. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +1 -3
  144. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -3
  145. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +2 -0
  146. data/lib/gen/temporal/api/enums/v1/update_pb.rb +6 -4
  147. data/lib/gen/temporal/api/history/v1/message_pb.rb +27 -19
  148. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -0
  149. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +3 -0
  150. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  151. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  152. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  153. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  154. data/lib/gen/temporal/api/update/v1/message_pb.rb +72 -0
  155. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +26 -16
  156. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  157. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  158. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  159. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  160. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  161. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  162. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +27 -21
  163. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +28 -24
  164. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  165. data/lib/temporalio/activity/context.rb +13 -8
  166. data/lib/temporalio/activity/info.rb +1 -1
  167. data/lib/temporalio/bridge/connect_options.rb +15 -0
  168. data/lib/temporalio/bridge/retry_config.rb +24 -0
  169. data/lib/temporalio/bridge/tls_options.rb +19 -0
  170. data/lib/temporalio/client/implementation.rb +8 -8
  171. data/lib/temporalio/connection/retry_config.rb +44 -0
  172. data/lib/temporalio/connection/service.rb +20 -0
  173. data/lib/temporalio/connection/test_service.rb +92 -0
  174. data/lib/temporalio/connection/tls_options.rb +51 -0
  175. data/lib/temporalio/connection/workflow_service.rb +731 -0
  176. data/lib/temporalio/connection.rb +55 -720
  177. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  178. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  179. data/lib/temporalio/interceptor/chain.rb +5 -5
  180. data/lib/temporalio/interceptor/client.rb +8 -4
  181. data/lib/temporalio/interceptor.rb +22 -0
  182. data/lib/temporalio/retry_policy.rb +13 -3
  183. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  184. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  185. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  186. data/lib/temporalio/testing.rb +175 -0
  187. data/lib/temporalio/version.rb +1 -1
  188. data/lib/temporalio/worker/activity_runner.rb +26 -4
  189. data/lib/temporalio/worker/activity_worker.rb +44 -18
  190. data/lib/temporalio/worker/sync_worker.rb +47 -11
  191. data/lib/temporalio/worker.rb +27 -21
  192. data/lib/temporalio/workflow/async.rb +46 -0
  193. data/lib/temporalio/workflow/future.rb +138 -0
  194. data/lib/temporalio/workflow/info.rb +76 -0
  195. data/temporalio.gemspec +4 -3
  196. metadata +67 -17
  197. data/bridge/sdk-core/Cargo.lock +0 -2606
  198. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
  199. data/lib/bridge.so +0 -0
  200. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +0 -25
  201. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +0 -49
  202. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
@@ -3,6 +3,8 @@ pub(crate) mod client;
3
3
  mod workflow;
4
4
 
5
5
  pub use temporal_sdk_core_api::worker::{WorkerConfig, WorkerConfigBuilder};
6
+ #[cfg(feature = "save_wf_inputs")]
7
+ pub use workflow::replay_wf_state_inputs;
6
8
 
7
9
  pub(crate) use activities::{
8
10
  ExecutingLAId, LocalActRequest, LocalActivityExecutionResult, LocalActivityResolution,
@@ -19,20 +21,30 @@ use crate::{
19
21
  WorkflowTaskPoller,
20
22
  },
21
23
  protosext::{validate_activity_completion, ValidPollWFTQResponse},
22
- telemetry::metrics::{
23
- activity_poller, local_activity_worker_type, workflow_poller, workflow_sticky_poller,
24
- MetricsContext,
24
+ telemetry::{
25
+ metrics::{
26
+ activity_poller, local_activity_worker_type, workflow_poller, workflow_sticky_poller,
27
+ MetricsContext,
28
+ },
29
+ TelemetryInstance,
25
30
  },
26
31
  worker::{
27
32
  activities::{DispatchOrTimeoutLA, LACompleteAction, LocalActivityManager},
28
33
  client::WorkerClient,
29
- workflow::{LocalResolution, WorkflowBasics, Workflows},
34
+ workflow::{LAReqSink, LocalResolution, WorkflowBasics, Workflows},
30
35
  },
31
36
  ActivityHeartbeat, CompleteActivityError, PollActivityError, PollWfError, WorkerTrait,
32
37
  };
33
38
  use activities::{LocalInFlightActInfo, WorkerActivityTasks};
34
39
  use futures::Stream;
35
- use std::{convert::TryInto, sync::Arc};
40
+ use std::{
41
+ convert::TryInto,
42
+ future,
43
+ sync::{
44
+ atomic::{AtomicBool, Ordering},
45
+ Arc,
46
+ },
47
+ };
36
48
  use temporal_sdk_core_protos::{
37
49
  coresdk::{
38
50
  activity_result::activity_execution_result,
@@ -44,10 +56,11 @@ use temporal_sdk_core_protos::{
44
56
  temporal::api::{
45
57
  enums::v1::TaskQueueKind,
46
58
  taskqueue::v1::{StickyExecutionAttributes, TaskQueue},
47
- workflowservice::v1::PollActivityTaskQueueResponse,
59
+ workflowservice::v1::{get_system_info_response, PollActivityTaskQueueResponse},
48
60
  },
49
61
  TaskToken,
50
62
  };
63
+ use tokio::sync::mpsc::unbounded_channel;
51
64
  use tokio_util::sync::CancellationToken;
52
65
 
53
66
  /// A worker polls on a certain task queue
@@ -66,6 +79,10 @@ pub struct Worker {
66
79
  /// Will be called at the end of each activation completion
67
80
  #[allow(clippy::type_complexity)] // Sorry clippy, there's no simple way to re-use here.
68
81
  post_activate_hook: Option<Box<dyn Fn(&Self, &str, usize) + Send + Sync>>,
82
+ /// Set when non-local activities are complete and should stop being polled
83
+ non_local_activities_complete: Arc<AtomicBool>,
84
+ /// Set when local activities are complete and should stop being polled
85
+ local_activities_complete: Arc<AtomicBool>,
69
86
  }
70
87
 
71
88
  #[async_trait::async_trait]
@@ -134,6 +151,9 @@ impl WorkerTrait for Worker {
134
151
  if let Some(atm) = self.at_task_mgr.as_ref() {
135
152
  atm.notify_shutdown();
136
153
  }
154
+ // Let the manager know that shutdown has been initiated to try to unblock the local activity poll in case this
155
+ // worker is an activity-only worker.
156
+ self.local_act_mgr.shutdown_initiated();
137
157
  info!(
138
158
  task_queue=%self.config.task_queue,
139
159
  namespace=%self.config.namespace,
@@ -156,11 +176,17 @@ impl Worker {
156
176
  config: WorkerConfig,
157
177
  sticky_queue_name: Option<String>,
158
178
  client: Arc<dyn WorkerClient>,
159
- metrics: MetricsContext,
179
+ telem_instance: Option<&TelemetryInstance>,
160
180
  ) -> Self {
161
181
  info!(task_queue=%config.task_queue,
162
182
  namespace=%config.namespace,
163
183
  "Initializing worker");
184
+ let metrics = if let Some(ti) = telem_instance {
185
+ MetricsContext::top_level(config.namespace.clone(), ti)
186
+ .with_task_q(config.task_queue.clone())
187
+ } else {
188
+ MetricsContext::no_op()
189
+ };
164
190
  metrics.worker_registered();
165
191
 
166
192
  let shutdown_token = CancellationToken::new();
@@ -223,31 +249,34 @@ impl Worker {
223
249
  wft_stream,
224
250
  act_poll_buffer,
225
251
  metrics,
252
+ telem_instance,
226
253
  shutdown_token,
227
254
  )
228
255
  }
229
256
 
230
257
  #[cfg(test)]
231
258
  pub(crate) fn new_test(config: WorkerConfig, client: impl WorkerClient + 'static) -> Self {
232
- Self::new(config, None, Arc::new(client), MetricsContext::no_op())
259
+ Self::new(config, None, Arc::new(client), None)
233
260
  }
234
261
 
262
+ #[allow(clippy::too_many_arguments)] // Not much worth combining here
235
263
  pub(crate) fn new_with_pollers(
236
- config: WorkerConfig,
264
+ mut config: WorkerConfig,
237
265
  sticky_queue_name: Option<String>,
238
266
  client: Arc<dyn WorkerClient>,
239
267
  wft_stream: impl Stream<Item = Result<ValidPollWFTQResponse, tonic::Status>> + Send + 'static,
240
268
  act_poller: Option<BoxedActPoller>,
241
269
  metrics: MetricsContext,
270
+ telem_instance: Option<&TelemetryInstance>,
242
271
  shutdown_token: CancellationToken,
243
272
  ) -> Self {
273
+ let (hb_tx, hb_rx) = unbounded_channel();
244
274
  let local_act_mgr = Arc::new(LocalActivityManager::new(
245
275
  config.max_outstanding_local_activities,
246
276
  config.namespace.clone(),
277
+ hb_tx,
247
278
  metrics.with_new_attrs([local_activity_worker_type()]),
248
279
  ));
249
- let lam_clone = local_act_mgr.clone();
250
- let local_act_req_sink = move |requests| lam_clone.enqueue(requests);
251
280
  let at_task_mgr = act_poller.map(|ap| {
252
281
  WorkerActivityTasks::new(
253
282
  config.max_outstanding_activities,
@@ -259,18 +288,20 @@ impl Worker {
259
288
  config.default_heartbeat_throttle_interval,
260
289
  )
261
290
  });
291
+ let poll_on_non_local_activities = at_task_mgr.is_some();
292
+ if !poll_on_non_local_activities {
293
+ info!("Activity polling is disabled for this worker");
294
+ };
295
+ let la_sink = LAReqSink::new(local_act_mgr.clone(), config.wf_state_inputs.clone());
262
296
  Self {
263
297
  wf_client: client.clone(),
264
298
  workflows: Workflows::new(
265
- WorkflowBasics {
266
- max_cached_workflows: config.max_cached_workflows,
267
- max_outstanding_wfts: config.max_outstanding_workflow_tasks,
268
- shutdown_token: shutdown_token.child_token(),
299
+ build_wf_basics(
300
+ &mut config,
269
301
  metrics,
270
- namespace: config.namespace.clone(),
271
- task_queue: config.task_queue.clone(),
272
- ignore_evicts_on_shutdown: config.ignore_evicts_on_shutdown,
273
- },
302
+ shutdown_token.child_token(),
303
+ client.capabilities().cloned().unwrap_or_default(),
304
+ ),
274
305
  sticky_queue_name.map(|sq| StickyExecutionAttributes {
275
306
  worker_task_queue: Some(TaskQueue {
276
307
  name: sq,
@@ -285,16 +316,22 @@ impl Worker {
285
316
  }),
286
317
  client,
287
318
  wft_stream,
288
- local_act_req_sink,
319
+ la_sink,
320
+ local_act_mgr.clone(),
321
+ hb_rx,
289
322
  at_task_mgr
290
323
  .as_ref()
291
324
  .map(|mgr| mgr.get_handle_for_workflows()),
325
+ telem_instance,
292
326
  ),
293
327
  at_task_mgr,
294
328
  local_act_mgr,
295
329
  config,
296
330
  shutdown_token,
297
331
  post_activate_hook: None,
332
+ // Complete if there configured not to poll on non-local activities.
333
+ non_local_activities_complete: Arc::new(AtomicBool::new(!poll_on_non_local_activities)),
334
+ local_activities_complete: Default::default(),
298
335
  }
299
336
  }
300
337
 
@@ -304,15 +341,16 @@ impl Worker {
304
341
  self.initiate_shutdown();
305
342
  // Next we need to wait for all local activities to finish so no more workflow task
306
343
  // heartbeats will be generated
307
- self.local_act_mgr.shutdown_and_wait_all_finished().await;
308
344
  // Wait for workflows to finish
309
345
  self.workflows
310
346
  .shutdown()
311
347
  .await
312
348
  .expect("Workflow processing terminates cleanly");
349
+ let lam = self.local_act_mgr.clone();
350
+ lam.wait_all_outstanding_tasks_finished().await;
313
351
  // Wait for activities to finish
314
352
  if let Some(acts) = self.at_task_mgr.as_ref() {
315
- acts.wait_all_finished().await;
353
+ acts.shutdown().await;
316
354
  }
317
355
  }
318
356
 
@@ -346,13 +384,9 @@ impl Worker {
346
384
  .unwrap_or_default()
347
385
  }
348
386
 
349
- #[cfg(test)]
387
+ #[allow(unused)]
350
388
  pub(crate) async fn available_wft_permits(&self) -> usize {
351
- self.workflows
352
- .get_state_info()
353
- .await
354
- .expect("You can only check for available permits before shutdown")
355
- .available_wft_permits
389
+ self.workflows.available_wft_permits()
356
390
  }
357
391
 
358
392
  /// Get new activity tasks (may be local or nonlocal). Local activities are returned first
@@ -361,35 +395,63 @@ impl Worker {
361
395
  /// Returns `Ok(None)` in the event of a poll timeout or if the polling loop should otherwise
362
396
  /// be restarted
363
397
  async fn activity_poll(&self) -> Result<Option<ActivityTask>, PollActivityError> {
398
+ let local_activities_complete = self.local_activities_complete.load(Ordering::Relaxed);
399
+ let non_local_activities_complete =
400
+ self.non_local_activities_complete.load(Ordering::Relaxed);
401
+ if local_activities_complete && non_local_activities_complete {
402
+ return Err(PollActivityError::ShutDown);
403
+ }
364
404
  let act_mgr_poll = async {
405
+ if non_local_activities_complete {
406
+ future::pending::<()>().await;
407
+ unreachable!()
408
+ }
365
409
  if let Some(ref act_mgr) = self.at_task_mgr {
366
- act_mgr.poll().await
410
+ let res = act_mgr.poll().await;
411
+ if let Err(err) = res.as_ref() {
412
+ if matches!(err, PollActivityError::ShutDown) {
413
+ self.non_local_activities_complete
414
+ .store(true, Ordering::Relaxed);
415
+ return Ok(None);
416
+ }
417
+ };
418
+ res.map(Some)
367
419
  } else {
368
- info!("Activity polling is disabled for this worker");
369
- self.shutdown_token.cancelled().await;
370
- Err(PollActivityError::ShutDown)
420
+ // We expect the local activity branch below to produce shutdown when appropriate if
421
+ // there are no activity pollers.
422
+ future::pending::<()>().await;
423
+ unreachable!()
424
+ }
425
+ };
426
+ let local_activities_poll = async {
427
+ if local_activities_complete {
428
+ future::pending::<()>().await;
429
+ unreachable!()
430
+ }
431
+ match self.local_act_mgr.next_pending().await {
432
+ Some(DispatchOrTimeoutLA::Dispatch(r)) => Ok(Some(r)),
433
+ Some(DispatchOrTimeoutLA::Timeout {
434
+ run_id,
435
+ resolution,
436
+ task,
437
+ }) => {
438
+ self.notify_local_result(&run_id, LocalResolution::LocalActivity(resolution));
439
+ Ok(task)
440
+ }
441
+ None => {
442
+ if self.shutdown_token.is_cancelled() {
443
+ self.local_activities_complete
444
+ .store(true, Ordering::Relaxed);
445
+ }
446
+ Ok(None)
447
+ }
371
448
  }
372
449
  };
373
450
 
374
451
  tokio::select! {
375
452
  biased;
376
453
 
377
- r = self.local_act_mgr.next_pending() => {
378
- match r {
379
- Some(DispatchOrTimeoutLA::Dispatch(r)) => Ok(Some(r)),
380
- Some(DispatchOrTimeoutLA::Timeout { run_id, resolution, task }) => {
381
- self.notify_local_result(
382
- &run_id, LocalResolution::LocalActivity(resolution));
383
- Ok(task)
384
- },
385
- None => {
386
- if self.shutdown_token.is_cancelled() {
387
- return Err(PollActivityError::ShutDown);
388
- }
389
- Ok(None)
390
- }
391
- }
392
- },
454
+ r = local_activities_poll => r,
393
455
  r = act_mgr_poll => r,
394
456
  }
395
457
  }
@@ -448,7 +510,16 @@ impl Worker {
448
510
 
449
511
  #[instrument(skip(self), fields(run_id, workflow_id, task_queue=%self.config.task_queue))]
450
512
  pub(crate) async fn next_workflow_activation(&self) -> Result<WorkflowActivation, PollWfError> {
451
- self.workflows.next_workflow_activation().await
513
+ let r = self.workflows.next_workflow_activation().await;
514
+ // In the event workflows are shutdown, begin shutdown of everything else, since that's
515
+ // about to happen anyway. Tell the local activity manager that, so that it can know to
516
+ // cancel any remaining outstanding LAs and shutdown.
517
+ if matches!(r, Err(PollWfError::ShutDown)) {
518
+ // This is covering the situation where WFT pollers dying is the reason for shutdown
519
+ self.initiate_shutdown();
520
+ self.local_act_mgr.workflows_have_shutdown();
521
+ }
522
+ r
452
523
  }
453
524
 
454
525
  #[instrument(skip(self, completion),
@@ -458,11 +529,14 @@ impl Worker {
458
529
  &self,
459
530
  completion: WorkflowActivationCompletion,
460
531
  ) -> Result<(), CompleteWfError> {
461
- let run_id = completion.run_id.clone();
462
- let most_recent_event = self.workflows.activation_completed(completion).await?;
463
- if let Some(h) = &self.post_activate_hook {
464
- h(self, &run_id, most_recent_event);
465
- }
532
+ self.workflows
533
+ .activation_completed(
534
+ completion,
535
+ self.post_activate_hook.as_ref().map(|h| {
536
+ |run_id: &str, most_recent_event: usize| h(self, run_id, most_recent_event)
537
+ }),
538
+ )
539
+ .await?;
466
540
  Ok(())
467
541
  }
468
542
 
@@ -508,14 +582,39 @@ impl Worker {
508
582
  }
509
583
  }
510
584
 
585
+ fn build_wf_basics(
586
+ config: &mut WorkerConfig,
587
+ metrics: MetricsContext,
588
+ shutdown_token: CancellationToken,
589
+ server_capabilities: get_system_info_response::Capabilities,
590
+ ) -> WorkflowBasics {
591
+ WorkflowBasics {
592
+ max_cached_workflows: config.max_cached_workflows,
593
+ max_outstanding_wfts: config.max_outstanding_workflow_tasks,
594
+ shutdown_token,
595
+ metrics,
596
+ namespace: config.namespace.clone(),
597
+ task_queue: config.task_queue.clone(),
598
+ ignore_evicts_on_shutdown: config.ignore_evicts_on_shutdown,
599
+ fetching_concurrency: config.fetching_concurrency,
600
+ server_capabilities,
601
+ #[cfg(feature = "save_wf_inputs")]
602
+ wf_state_inputs: config.wf_state_inputs.take(),
603
+ }
604
+ }
605
+
511
606
  #[cfg(test)]
512
607
  mod tests {
513
608
  use super::*;
514
- use crate::{test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client};
609
+ use crate::{
610
+ advance_fut, test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client,
611
+ };
612
+ use futures::FutureExt;
613
+
515
614
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
516
615
 
517
616
  #[tokio::test]
518
- async fn activity_timeouts_dont_eat_permits() {
617
+ async fn activity_timeouts_maintain_permit() {
519
618
  let mut mock_client = mock_workflow_client();
520
619
  mock_client
521
620
  .expect_poll_activity_task()
@@ -526,8 +625,16 @@ mod tests {
526
625
  .build()
527
626
  .unwrap();
528
627
  let worker = Worker::new_test(cfg, mock_client);
529
- assert_eq!(worker.activity_poll().await.unwrap(), None);
530
- assert_eq!(worker.at_task_mgr.unwrap().remaining_activity_capacity(), 5);
628
+ let fut = worker.poll_activity_task();
629
+ advance_fut!(fut);
630
+ assert_eq!(
631
+ worker
632
+ .at_task_mgr
633
+ .as_ref()
634
+ .unwrap()
635
+ .remaining_activity_capacity(),
636
+ 4
637
+ );
531
638
  }
532
639
 
533
640
  #[tokio::test]
@@ -25,11 +25,9 @@ impl WorkflowBridge {
25
25
  }
26
26
  }
27
27
 
28
- #[async_trait::async_trait]
29
28
  impl WorkflowFetcher for WorkflowBridge {
30
- async fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
29
+ fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
31
30
  let in_cmds = self.incoming_commands.try_recv();
32
-
33
31
  let in_cmds = in_cmds.unwrap_or_else(|_| vec![WFCommand::NoCommandsFromLang]);
34
32
  debug!(in_cmds = %in_cmds.display(), "wf bridge iteration fetch");
35
33
  in_cmds
@@ -78,16 +78,14 @@ impl DrivenWorkflow {
78
78
  }
79
79
  }
80
80
 
81
- #[async_trait::async_trait]
82
81
  impl WorkflowFetcher for DrivenWorkflow {
83
- async fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
84
- self.fetcher.fetch_workflow_iteration_output().await
82
+ fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
83
+ self.fetcher.fetch_workflow_iteration_output()
85
84
  }
86
85
  }
87
86
 
88
87
  /// Implementors of this trait represent a way to fetch output from executing/iterating some
89
88
  /// workflow code (or a mocked workflow).
90
- #[async_trait::async_trait]
91
89
  pub trait WorkflowFetcher: Send {
92
90
  /// Obtain any output from the workflow's recent execution(s). Because the lang sdk is
93
91
  /// responsible for calling workflow code as a result of receiving tasks from
@@ -97,5 +95,5 @@ pub trait WorkflowFetcher: Send {
97
95
  ///
98
96
  /// In the case of the real [WorkflowBridge] implementation, commands are simply pulled from
99
97
  /// a buffer that the language side sinks into when it calls [crate::Core::complete_task]
100
- async fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand>;
98
+ fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand>;
101
99
  }