temporalio 0.0.2 → 0.1.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 (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
  }