@temporalio/core-bridge 1.5.2 → 1.7.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 (194) hide show
  1. package/Cargo.lock +304 -112
  2. package/lib/index.d.ts +8 -6
  3. package/lib/index.js.map +1 -1
  4. package/package.json +9 -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/.buildkite/docker/Dockerfile +2 -2
  11. package/sdk-core/.buildkite/docker/docker-compose.yaml +1 -1
  12. package/sdk-core/.buildkite/pipeline.yml +2 -4
  13. package/sdk-core/.cargo/config.toml +5 -2
  14. package/sdk-core/.github/workflows/heavy.yml +29 -0
  15. package/sdk-core/Cargo.toml +1 -1
  16. package/sdk-core/README.md +20 -10
  17. package/sdk-core/client/src/lib.rs +215 -39
  18. package/sdk-core/client/src/metrics.rs +17 -8
  19. package/sdk-core/client/src/raw.rs +4 -4
  20. package/sdk-core/client/src/retry.rs +32 -20
  21. package/sdk-core/core/Cargo.toml +25 -12
  22. package/sdk-core/core/src/abstractions/take_cell.rs +28 -0
  23. package/sdk-core/core/src/abstractions.rs +204 -14
  24. package/sdk-core/core/src/core_tests/activity_tasks.rs +143 -50
  25. package/sdk-core/core/src/core_tests/child_workflows.rs +6 -5
  26. package/sdk-core/core/src/core_tests/determinism.rs +165 -2
  27. package/sdk-core/core/src/core_tests/local_activities.rs +431 -43
  28. package/sdk-core/core/src/core_tests/queries.rs +34 -16
  29. package/sdk-core/core/src/core_tests/workers.rs +8 -5
  30. package/sdk-core/core/src/core_tests/workflow_tasks.rs +588 -55
  31. package/sdk-core/core/src/ephemeral_server/mod.rs +113 -12
  32. package/sdk-core/core/src/internal_flags.rs +155 -0
  33. package/sdk-core/core/src/lib.rs +16 -9
  34. package/sdk-core/core/src/protosext/mod.rs +1 -1
  35. package/sdk-core/core/src/replay/mod.rs +16 -27
  36. package/sdk-core/core/src/telemetry/log_export.rs +1 -1
  37. package/sdk-core/core/src/telemetry/metrics.rs +69 -35
  38. package/sdk-core/core/src/telemetry/mod.rs +60 -21
  39. package/sdk-core/core/src/telemetry/prometheus_server.rs +19 -13
  40. package/sdk-core/core/src/test_help/mod.rs +73 -14
  41. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +119 -160
  42. package/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  43. package/sdk-core/core/src/worker/activities/local_activities.rs +379 -129
  44. package/sdk-core/core/src/worker/activities.rs +350 -175
  45. package/sdk-core/core/src/worker/client/mocks.rs +22 -2
  46. package/sdk-core/core/src/worker/client.rs +18 -2
  47. package/sdk-core/core/src/worker/mod.rs +183 -64
  48. package/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  49. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  50. package/sdk-core/core/src/worker/workflow/history_update.rs +916 -277
  51. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +216 -183
  52. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +9 -12
  53. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +7 -9
  54. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +160 -87
  55. package/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +13 -14
  56. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +7 -9
  57. package/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +14 -17
  58. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +242 -110
  59. package/sdk-core/core/src/worker/workflow/machines/mod.rs +27 -19
  60. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +9 -11
  61. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +321 -206
  62. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +13 -18
  63. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +20 -29
  64. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  65. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +257 -51
  66. package/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +6 -17
  67. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +310 -150
  68. package/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +17 -20
  69. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +31 -15
  70. package/sdk-core/core/src/worker/workflow/managed_run.rs +1052 -380
  71. package/sdk-core/core/src/worker/workflow/mod.rs +598 -390
  72. package/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  73. package/sdk-core/core/src/worker/workflow/wft_extraction.rs +137 -0
  74. package/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  75. package/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  76. package/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  77. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +469 -718
  78. package/sdk-core/core-api/Cargo.toml +2 -1
  79. package/sdk-core/core-api/src/errors.rs +1 -34
  80. package/sdk-core/core-api/src/lib.rs +19 -9
  81. package/sdk-core/core-api/src/telemetry.rs +4 -6
  82. package/sdk-core/core-api/src/worker.rs +19 -1
  83. package/sdk-core/etc/deps.svg +115 -140
  84. package/sdk-core/etc/regen-depgraph.sh +5 -0
  85. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +86 -61
  86. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +29 -71
  87. package/sdk-core/histories/ends_empty_wft_complete.bin +0 -0
  88. package/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  89. package/sdk-core/histories/old_change_marker_format.bin +0 -0
  90. package/sdk-core/protos/api_upstream/.github/CODEOWNERS +2 -1
  91. package/sdk-core/protos/api_upstream/Makefile +6 -6
  92. package/sdk-core/protos/api_upstream/build/go.mod +7 -0
  93. package/sdk-core/protos/api_upstream/build/go.sum +5 -0
  94. package/sdk-core/protos/api_upstream/build/tools.go +29 -0
  95. package/sdk-core/protos/api_upstream/go.mod +6 -0
  96. package/sdk-core/protos/api_upstream/temporal/api/batch/v1/message.proto +9 -2
  97. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +7 -26
  98. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +13 -2
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -2
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +3 -7
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +3 -2
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +8 -8
  103. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +25 -2
  104. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +2 -2
  105. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/query.proto +2 -2
  106. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +2 -2
  107. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/schedule.proto +2 -2
  108. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +2 -2
  109. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +24 -19
  110. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +2 -2
  111. package/sdk-core/protos/api_upstream/temporal/api/errordetails/v1/message.proto +2 -2
  112. package/sdk-core/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
  113. package/sdk-core/protos/api_upstream/temporal/api/filter/v1/message.proto +2 -2
  114. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +49 -26
  115. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +4 -2
  116. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +5 -2
  117. package/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/service.proto +2 -2
  118. package/sdk-core/protos/api_upstream/temporal/api/protocol/v1/message.proto +57 -0
  119. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +2 -2
  120. package/sdk-core/protos/api_upstream/temporal/api/replication/v1/message.proto +2 -2
  121. package/sdk-core/protos/api_upstream/temporal/api/schedule/v1/message.proto +2 -2
  122. package/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  123. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -2
  124. package/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +71 -6
  125. package/sdk-core/protos/api_upstream/temporal/api/version/v1/message.proto +2 -2
  126. package/sdk-core/protos/api_upstream/temporal/api/workflow/v1/message.proto +2 -2
  127. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +64 -28
  128. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +4 -4
  129. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +7 -8
  130. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +10 -7
  131. package/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +19 -30
  132. package/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  133. package/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  134. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +8 -0
  135. package/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +67 -60
  136. package/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +85 -84
  137. package/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +9 -3
  138. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +2 -2
  139. package/sdk-core/protos/testsrv_upstream/temporal/api/testservice/v1/service.proto +2 -2
  140. package/sdk-core/sdk/Cargo.toml +5 -4
  141. package/sdk-core/sdk/src/lib.rs +108 -26
  142. package/sdk-core/sdk/src/workflow_context/options.rs +7 -1
  143. package/sdk-core/sdk/src/workflow_context.rs +24 -17
  144. package/sdk-core/sdk/src/workflow_future.rs +16 -15
  145. package/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  146. package/sdk-core/sdk-core-protos/build.rs +36 -2
  147. package/sdk-core/sdk-core-protos/src/history_builder.rs +138 -106
  148. package/sdk-core/sdk-core-protos/src/history_info.rs +10 -1
  149. package/sdk-core/sdk-core-protos/src/lib.rs +272 -87
  150. package/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  151. package/sdk-core/test-utils/Cargo.toml +3 -1
  152. package/sdk-core/test-utils/src/canned_histories.rs +106 -296
  153. package/sdk-core/test-utils/src/histfetch.rs +1 -1
  154. package/sdk-core/test-utils/src/lib.rs +82 -23
  155. package/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  156. package/sdk-core/test-utils/src/workflows.rs +29 -0
  157. package/sdk-core/tests/fuzzy_workflow.rs +130 -0
  158. package/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  159. package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  160. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +10 -5
  161. package/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  162. package/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  163. package/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  164. package/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  165. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +161 -72
  166. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  167. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  168. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +80 -3
  169. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  170. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  171. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +94 -200
  172. package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +2 -4
  173. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +34 -28
  174. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +76 -7
  175. package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  176. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  177. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  178. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  179. package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +7 -8
  180. package/sdk-core/tests/integ_tests/workflow_tests.rs +13 -14
  181. package/sdk-core/tests/main.rs +3 -13
  182. package/sdk-core/tests/runner.rs +75 -36
  183. package/sdk-core/tests/wf_input_replay.rs +32 -0
  184. package/src/conversions.rs +14 -8
  185. package/src/runtime.rs +9 -8
  186. package/ts/index.ts +8 -6
  187. package/sdk-core/bridge-ffi/Cargo.toml +0 -24
  188. package/sdk-core/bridge-ffi/LICENSE.txt +0 -23
  189. package/sdk-core/bridge-ffi/build.rs +0 -25
  190. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +0 -224
  191. package/sdk-core/bridge-ffi/src/lib.rs +0 -746
  192. package/sdk-core/bridge-ffi/src/wrappers.rs +0 -221
  193. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +0 -210
  194. package/sdk-core/sdk/src/conversions.rs +0 -8
@@ -1,15 +1,33 @@
1
1
  use super::*;
2
2
  use futures::Future;
3
3
 
4
+ pub(crate) static DEFAULT_TEST_CAPABILITIES: &Capabilities = &Capabilities {
5
+ signal_and_query_header: true,
6
+ internal_error_differentiation: true,
7
+ activity_failure_include_heartbeat: true,
8
+ supports_schedules: true,
9
+ encoded_failure_attributes: true,
10
+ build_id_based_versioning: true,
11
+ upsert_memo: true,
12
+ eager_workflow_start: true,
13
+ sdk_metadata: true,
14
+ };
15
+
4
16
  #[cfg(test)]
5
17
  /// Create a mock client primed with basic necessary expectations
6
18
  pub(crate) fn mock_workflow_client() -> MockWorkerClient {
7
- MockWorkerClient::new()
19
+ let mut r = MockWorkerClient::new();
20
+ r.expect_capabilities()
21
+ .returning(|| Some(DEFAULT_TEST_CAPABILITIES));
22
+ r
8
23
  }
9
24
 
10
25
  /// Create a mock manual client primed with basic necessary expectations
11
26
  pub(crate) fn mock_manual_workflow_client() -> MockManualWorkerClient {
12
- MockManualWorkerClient::new()
27
+ let mut r = MockManualWorkerClient::new();
28
+ r.expect_capabilities()
29
+ .returning(|| Some(DEFAULT_TEST_CAPABILITIES));
30
+ r
13
31
  }
14
32
 
15
33
  // Need a version of the mock that can return futures so we can return potentially pending
@@ -83,5 +101,7 @@ mockall::mock! {
83
101
  query_result: QueryResult,
84
102
  ) -> impl Future<Output = Result<RespondQueryTaskCompletedResponse>> + Send + 'b
85
103
  where 'a: 'b, Self: 'b;
104
+
105
+ fn capabilities(&self) -> Option<&'static get_system_info_response::Capabilities>;
86
106
  }
87
107
  }
@@ -7,12 +7,13 @@ use temporal_sdk_core_protos::{
7
7
  coresdk::workflow_commands::QueryResult,
8
8
  temporal::api::{
9
9
  command::v1::Command,
10
- common::v1::{Payloads, WorkflowExecution},
10
+ common::v1::{MeteringMetadata, Payloads, WorkflowExecution},
11
11
  enums::v1::{TaskQueueKind, WorkflowTaskFailedCause},
12
12
  failure::v1::Failure,
13
13
  query::v1::WorkflowQueryResult,
14
+ sdk::v1::WorkflowTaskCompletedMetadata,
14
15
  taskqueue::v1::{StickyExecutionAttributes, TaskQueue, TaskQueueMetadata, VersionId},
15
- workflowservice::v1::*,
16
+ workflowservice::v1::{get_system_info_response::Capabilities, *},
16
17
  },
17
18
  TaskToken,
18
19
  };
@@ -109,6 +110,9 @@ pub(crate) trait WorkerClient: Sync + Send {
109
110
  task_token: TaskToken,
110
111
  query_result: QueryResult,
111
112
  ) -> Result<RespondQueryTaskCompletedResponse>;
113
+
114
+ #[allow(clippy::needless_lifetimes)] // Clippy is wrong here
115
+ fn capabilities<'a>(&'a self) -> Option<&'a get_system_info_response::Capabilities>;
112
116
  }
113
117
 
114
118
  #[async_trait::async_trait]
@@ -189,6 +193,7 @@ impl WorkerClient for WorkerClientBag {
189
193
  worker_versioning_id: Some(VersionId {
190
194
  worker_build_id: self.versioning_build_id(),
191
195
  }),
196
+ messages: vec![],
192
197
  binary_checksum: self.worker_build_id.clone(),
193
198
  query_results: request
194
199
  .query_responses
@@ -206,6 +211,8 @@ impl WorkerClient for WorkerClientBag {
206
211
  })
207
212
  .collect(),
208
213
  namespace: self.namespace.clone(),
214
+ sdk_metadata: Some(request.sdk_metadata),
215
+ metering_metadata: Some(request.metering_metadata),
209
216
  };
210
217
  Ok(self
211
218
  .client
@@ -302,6 +309,7 @@ impl WorkerClient for WorkerClientBag {
302
309
  identity: self.identity.clone(),
303
310
  binary_checksum: self.worker_build_id.clone(),
304
311
  namespace: self.namespace.clone(),
312
+ messages: vec![],
305
313
  };
306
314
  Ok(self
307
315
  .client
@@ -352,6 +360,10 @@ impl WorkerClient for WorkerClientBag {
352
360
  .await?
353
361
  .into_inner())
354
362
  }
363
+
364
+ fn capabilities(&self) -> Option<&Capabilities> {
365
+ self.client.get_client().inner().capabilities()
366
+ }
355
367
  }
356
368
 
357
369
  /// A version of [RespondWorkflowTaskCompletedRequest] that will finish being filled out by the
@@ -370,4 +382,8 @@ pub(crate) struct WorkflowTaskCompletion {
370
382
  pub return_new_workflow_task: bool,
371
383
  /// Force a new WFT to be created after this completion
372
384
  pub force_create_new_workflow_task: bool,
385
+ /// SDK-specific metadata to send
386
+ pub sdk_metadata: WorkflowTaskCompletedMetadata,
387
+ /// Metering info
388
+ pub metering_metadata: MeteringMetadata,
373
389
  }
@@ -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
@@ -65,7 +78,11 @@ pub struct Worker {
65
78
  shutdown_token: CancellationToken,
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
- post_activate_hook: Option<Box<dyn Fn(&Self, &str, usize) + Send + Sync>>,
81
+ post_activate_hook: Option<Box<dyn Fn(&Self, PostActivateHookData) + 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]
@@ -132,7 +149,13 @@ impl WorkerTrait for Worker {
132
149
  self.shutdown_token.cancel();
133
150
  // First, we want to stop polling of both activity and workflow tasks
134
151
  if let Some(atm) = self.at_task_mgr.as_ref() {
135
- atm.notify_shutdown();
152
+ atm.initiate_shutdown();
153
+ }
154
+ // Let the manager know that shutdown has been initiated to try to unblock the local
155
+ // activity poll in case this worker is an activity-only worker.
156
+ self.local_act_mgr.shutdown_initiated();
157
+ if !self.workflows.ever_polled() {
158
+ self.local_act_mgr.workflows_have_shutdown();
136
159
  }
137
160
  info!(
138
161
  task_queue=%self.config.task_queue,
@@ -156,11 +179,17 @@ impl Worker {
156
179
  config: WorkerConfig,
157
180
  sticky_queue_name: Option<String>,
158
181
  client: Arc<dyn WorkerClient>,
159
- metrics: MetricsContext,
182
+ telem_instance: Option<&TelemetryInstance>,
160
183
  ) -> Self {
161
184
  info!(task_queue=%config.task_queue,
162
185
  namespace=%config.namespace,
163
186
  "Initializing worker");
187
+ let metrics = if let Some(ti) = telem_instance {
188
+ MetricsContext::top_level(config.namespace.clone(), ti)
189
+ .with_task_q(config.task_queue.clone())
190
+ } else {
191
+ MetricsContext::no_op()
192
+ };
164
193
  metrics.worker_registered();
165
194
 
166
195
  let shutdown_token = CancellationToken::new();
@@ -223,31 +252,34 @@ impl Worker {
223
252
  wft_stream,
224
253
  act_poll_buffer,
225
254
  metrics,
255
+ telem_instance,
226
256
  shutdown_token,
227
257
  )
228
258
  }
229
259
 
230
260
  #[cfg(test)]
231
261
  pub(crate) fn new_test(config: WorkerConfig, client: impl WorkerClient + 'static) -> Self {
232
- Self::new(config, None, Arc::new(client), MetricsContext::no_op())
262
+ Self::new(config, None, Arc::new(client), None)
233
263
  }
234
264
 
265
+ #[allow(clippy::too_many_arguments)] // Not much worth combining here
235
266
  pub(crate) fn new_with_pollers(
236
- config: WorkerConfig,
267
+ mut config: WorkerConfig,
237
268
  sticky_queue_name: Option<String>,
238
269
  client: Arc<dyn WorkerClient>,
239
270
  wft_stream: impl Stream<Item = Result<ValidPollWFTQResponse, tonic::Status>> + Send + 'static,
240
271
  act_poller: Option<BoxedActPoller>,
241
272
  metrics: MetricsContext,
273
+ telem_instance: Option<&TelemetryInstance>,
242
274
  shutdown_token: CancellationToken,
243
275
  ) -> Self {
276
+ let (hb_tx, hb_rx) = unbounded_channel();
244
277
  let local_act_mgr = Arc::new(LocalActivityManager::new(
245
278
  config.max_outstanding_local_activities,
246
279
  config.namespace.clone(),
280
+ hb_tx,
247
281
  metrics.with_new_attrs([local_activity_worker_type()]),
248
282
  ));
249
- let lam_clone = local_act_mgr.clone();
250
- let local_act_req_sink = move |requests| lam_clone.enqueue(requests);
251
283
  let at_task_mgr = act_poller.map(|ap| {
252
284
  WorkerActivityTasks::new(
253
285
  config.max_outstanding_activities,
@@ -257,20 +289,23 @@ impl Worker {
257
289
  metrics.clone(),
258
290
  config.max_heartbeat_throttle_interval,
259
291
  config.default_heartbeat_throttle_interval,
292
+ config.graceful_shutdown_period,
260
293
  )
261
294
  });
295
+ let poll_on_non_local_activities = at_task_mgr.is_some();
296
+ if !poll_on_non_local_activities {
297
+ info!("Activity polling is disabled for this worker");
298
+ };
299
+ let la_sink = LAReqSink::new(local_act_mgr.clone(), config.wf_state_inputs.clone());
262
300
  Self {
263
301
  wf_client: client.clone(),
264
302
  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(),
303
+ build_wf_basics(
304
+ &mut config,
269
305
  metrics,
270
- namespace: config.namespace.clone(),
271
- task_queue: config.task_queue.clone(),
272
- ignore_evicts_on_shutdown: config.ignore_evicts_on_shutdown,
273
- },
306
+ shutdown_token.child_token(),
307
+ client.capabilities().cloned().unwrap_or_default(),
308
+ ),
274
309
  sticky_queue_name.map(|sq| StickyExecutionAttributes {
275
310
  worker_task_queue: Some(TaskQueue {
276
311
  name: sq,
@@ -285,16 +320,22 @@ impl Worker {
285
320
  }),
286
321
  client,
287
322
  wft_stream,
288
- local_act_req_sink,
323
+ la_sink,
324
+ local_act_mgr.clone(),
325
+ hb_rx,
289
326
  at_task_mgr
290
327
  .as_ref()
291
328
  .map(|mgr| mgr.get_handle_for_workflows()),
329
+ telem_instance,
292
330
  ),
293
331
  at_task_mgr,
294
332
  local_act_mgr,
295
333
  config,
296
334
  shutdown_token,
297
335
  post_activate_hook: None,
336
+ // Complete if there configured not to poll on non-local activities.
337
+ non_local_activities_complete: Arc::new(AtomicBool::new(!poll_on_non_local_activities)),
338
+ local_activities_complete: Default::default(),
298
339
  }
299
340
  }
300
341
 
@@ -302,9 +343,11 @@ impl Worker {
302
343
  /// completed
303
344
  async fn shutdown(&self) {
304
345
  self.initiate_shutdown();
305
- // Next we need to wait for all local activities to finish so no more workflow task
306
- // heartbeats will be generated
307
- self.local_act_mgr.shutdown_and_wait_all_finished().await;
346
+ // We need to wait for all local activities to finish so no more workflow task heartbeats
347
+ // will be generated
348
+ self.local_act_mgr
349
+ .wait_all_outstanding_tasks_finished()
350
+ .await;
308
351
  // Wait for workflows to finish
309
352
  self.workflows
310
353
  .shutdown()
@@ -312,12 +355,13 @@ impl Worker {
312
355
  .expect("Workflow processing terminates cleanly");
313
356
  // Wait for activities to finish
314
357
  if let Some(acts) = self.at_task_mgr.as_ref() {
315
- acts.wait_all_finished().await;
358
+ acts.shutdown().await;
316
359
  }
317
360
  }
318
361
 
319
362
  /// Finish shutting down by consuming the background pollers and freeing all resources
320
363
  async fn finalize_shutdown(self) {
364
+ self.shutdown().await;
321
365
  if let Some(b) = self.at_task_mgr {
322
366
  b.shutdown().await;
323
367
  }
@@ -346,13 +390,9 @@ impl Worker {
346
390
  .unwrap_or_default()
347
391
  }
348
392
 
349
- #[cfg(test)]
393
+ #[allow(unused)]
350
394
  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
395
+ self.workflows.available_wft_permits()
356
396
  }
357
397
 
358
398
  /// Get new activity tasks (may be local or nonlocal). Local activities are returned first
@@ -361,35 +401,63 @@ impl Worker {
361
401
  /// Returns `Ok(None)` in the event of a poll timeout or if the polling loop should otherwise
362
402
  /// be restarted
363
403
  async fn activity_poll(&self) -> Result<Option<ActivityTask>, PollActivityError> {
404
+ let local_activities_complete = self.local_activities_complete.load(Ordering::Relaxed);
405
+ let non_local_activities_complete =
406
+ self.non_local_activities_complete.load(Ordering::Relaxed);
407
+ if local_activities_complete && non_local_activities_complete {
408
+ return Err(PollActivityError::ShutDown);
409
+ }
364
410
  let act_mgr_poll = async {
411
+ if non_local_activities_complete {
412
+ future::pending::<()>().await;
413
+ unreachable!()
414
+ }
365
415
  if let Some(ref act_mgr) = self.at_task_mgr {
366
- act_mgr.poll().await
416
+ let res = act_mgr.poll().await;
417
+ if let Err(err) = res.as_ref() {
418
+ if matches!(err, PollActivityError::ShutDown) {
419
+ self.non_local_activities_complete
420
+ .store(true, Ordering::Relaxed);
421
+ return Ok(None);
422
+ }
423
+ };
424
+ res.map(Some)
367
425
  } else {
368
- info!("Activity polling is disabled for this worker");
369
- self.shutdown_token.cancelled().await;
370
- Err(PollActivityError::ShutDown)
426
+ // We expect the local activity branch below to produce shutdown when appropriate if
427
+ // there are no activity pollers.
428
+ future::pending::<()>().await;
429
+ unreachable!()
430
+ }
431
+ };
432
+ let local_activities_poll = async {
433
+ if local_activities_complete {
434
+ future::pending::<()>().await;
435
+ unreachable!()
436
+ }
437
+ match self.local_act_mgr.next_pending().await {
438
+ Some(DispatchOrTimeoutLA::Dispatch(r)) => Ok(Some(r)),
439
+ Some(DispatchOrTimeoutLA::Timeout {
440
+ run_id,
441
+ resolution,
442
+ task,
443
+ }) => {
444
+ self.notify_local_result(&run_id, LocalResolution::LocalActivity(resolution));
445
+ Ok(task)
446
+ }
447
+ None => {
448
+ if self.shutdown_token.is_cancelled() {
449
+ self.local_activities_complete
450
+ .store(true, Ordering::Relaxed);
451
+ }
452
+ Ok(None)
453
+ }
371
454
  }
372
455
  };
373
456
 
374
457
  tokio::select! {
375
458
  biased;
376
459
 
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
- },
460
+ r = local_activities_poll => r,
393
461
  r = act_mgr_poll => r,
394
462
  }
395
463
  }
@@ -448,7 +516,16 @@ impl Worker {
448
516
 
449
517
  #[instrument(skip(self), fields(run_id, workflow_id, task_queue=%self.config.task_queue))]
450
518
  pub(crate) async fn next_workflow_activation(&self) -> Result<WorkflowActivation, PollWfError> {
451
- self.workflows.next_workflow_activation().await
519
+ let r = self.workflows.next_workflow_activation().await;
520
+ // In the event workflows are shutdown, begin shutdown of everything else, since that's
521
+ // about to happen anyway. Tell the local activity manager that, so that it can know to
522
+ // cancel any remaining outstanding LAs and shutdown.
523
+ if matches!(r, Err(PollWfError::ShutDown)) {
524
+ // This is covering the situation where WFT pollers dying is the reason for shutdown
525
+ self.initiate_shutdown();
526
+ self.local_act_mgr.workflows_have_shutdown();
527
+ }
528
+ r
452
529
  }
453
530
 
454
531
  #[instrument(skip(self, completion),
@@ -458,11 +535,14 @@ impl Worker {
458
535
  &self,
459
536
  completion: WorkflowActivationCompletion,
460
537
  ) -> 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
- }
538
+ self.workflows
539
+ .activation_completed(
540
+ completion,
541
+ self.post_activate_hook
542
+ .as_ref()
543
+ .map(|h| |data: PostActivateHookData| h(self, data)),
544
+ )
545
+ .await?;
466
546
  Ok(())
467
547
  }
468
548
 
@@ -479,7 +559,7 @@ impl Worker {
479
559
  /// Sets a function to be called at the end of each activation completion
480
560
  pub(crate) fn set_post_activate_hook(
481
561
  &mut self,
482
- callback: impl Fn(&Self, &str, usize) + Send + Sync + 'static,
562
+ callback: impl Fn(&Self, PostActivateHookData) + Send + Sync + 'static,
483
563
  ) {
484
564
  self.post_activate_hook = Some(Box::new(callback))
485
565
  }
@@ -498,7 +578,7 @@ impl Worker {
498
578
  runtime: info.dispatch_time.elapsed(),
499
579
  attempt: info.attempt,
500
580
  backoff,
501
- original_schedule_time: Some(info.la_info.schedule_time),
581
+ original_schedule_time: info.la_info.schedule_cmd.original_schedule_time,
502
582
  }),
503
583
  )
504
584
  }
@@ -508,14 +588,45 @@ impl Worker {
508
588
  }
509
589
  }
510
590
 
591
+ pub struct PostActivateHookData<'a> {
592
+ pub run_id: &'a str,
593
+ pub most_recent_event: usize,
594
+ pub replaying: bool,
595
+ }
596
+
597
+ fn build_wf_basics(
598
+ config: &mut WorkerConfig,
599
+ metrics: MetricsContext,
600
+ shutdown_token: CancellationToken,
601
+ server_capabilities: get_system_info_response::Capabilities,
602
+ ) -> WorkflowBasics {
603
+ WorkflowBasics {
604
+ max_cached_workflows: config.max_cached_workflows,
605
+ max_outstanding_wfts: config.max_outstanding_workflow_tasks,
606
+ shutdown_token,
607
+ metrics,
608
+ namespace: config.namespace.clone(),
609
+ task_queue: config.task_queue.clone(),
610
+ ignore_evicts_on_shutdown: config.ignore_evicts_on_shutdown,
611
+ fetching_concurrency: config.fetching_concurrency,
612
+ server_capabilities,
613
+ #[cfg(feature = "save_wf_inputs")]
614
+ wf_state_inputs: config.wf_state_inputs.take(),
615
+ }
616
+ }
617
+
511
618
  #[cfg(test)]
512
619
  mod tests {
513
620
  use super::*;
514
- use crate::{test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client};
621
+ use crate::{
622
+ advance_fut, test_help::test_worker_cfg, worker::client::mocks::mock_workflow_client,
623
+ };
624
+ use futures::FutureExt;
625
+
515
626
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse;
516
627
 
517
628
  #[tokio::test]
518
- async fn activity_timeouts_dont_eat_permits() {
629
+ async fn activity_timeouts_maintain_permit() {
519
630
  let mut mock_client = mock_workflow_client();
520
631
  mock_client
521
632
  .expect_poll_activity_task()
@@ -526,8 +637,16 @@ mod tests {
526
637
  .build()
527
638
  .unwrap();
528
639
  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);
640
+ let fut = worker.poll_activity_task();
641
+ advance_fut!(fut);
642
+ assert_eq!(
643
+ worker
644
+ .at_task_mgr
645
+ .as_ref()
646
+ .unwrap()
647
+ .remaining_activity_capacity(),
648
+ 4
649
+ );
531
650
  }
532
651
 
533
652
  #[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
  }