@temporalio/core-bridge 1.8.5 → 1.9.0-rc.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 (196) hide show
  1. package/Cargo.lock +189 -152
  2. package/Cargo.toml +1 -0
  3. package/lib/index.d.ts +17 -44
  4. package/lib/index.js.map +1 -1
  5. package/package.json +3 -4
  6. package/releases/aarch64-apple-darwin/index.node +0 -0
  7. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  8. package/releases/x86_64-apple-darwin/index.node +0 -0
  9. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  10. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  11. package/sdk-core/.github/workflows/heavy.yml +4 -0
  12. package/sdk-core/.github/workflows/per-pr.yml +96 -0
  13. package/sdk-core/ARCHITECTURE.md +1 -1
  14. package/sdk-core/Cargo.toml +6 -0
  15. package/sdk-core/README.md +37 -21
  16. package/sdk-core/client/Cargo.toml +6 -3
  17. package/sdk-core/client/src/lib.rs +272 -138
  18. package/sdk-core/client/src/metrics.rs +68 -57
  19. package/sdk-core/client/src/raw.rs +191 -45
  20. package/sdk-core/client/src/retry.rs +20 -0
  21. package/sdk-core/client/src/worker_registry/mod.rs +264 -0
  22. package/sdk-core/client/src/workflow_handle/mod.rs +2 -1
  23. package/sdk-core/core/Cargo.toml +16 -18
  24. package/sdk-core/core/src/core_tests/child_workflows.rs +7 -7
  25. package/sdk-core/core/src/core_tests/mod.rs +1 -0
  26. package/sdk-core/core/src/core_tests/replay_flag.rs +29 -39
  27. package/sdk-core/core/src/core_tests/updates.rs +73 -0
  28. package/sdk-core/core/src/core_tests/workflow_tasks.rs +52 -1
  29. package/sdk-core/core/src/ephemeral_server/mod.rs +34 -11
  30. package/sdk-core/core/src/internal_flags.rs +7 -1
  31. package/sdk-core/core/src/lib.rs +19 -36
  32. package/sdk-core/core/src/protosext/mod.rs +11 -3
  33. package/sdk-core/core/src/protosext/protocol_messages.rs +102 -0
  34. package/sdk-core/core/src/replay/mod.rs +100 -48
  35. package/sdk-core/core/src/telemetry/log_export.rs +161 -28
  36. package/sdk-core/core/src/telemetry/metrics.rs +869 -248
  37. package/sdk-core/core/src/telemetry/mod.rs +135 -239
  38. package/sdk-core/core/src/telemetry/prometheus_server.rs +36 -31
  39. package/sdk-core/core/src/test_help/mod.rs +63 -4
  40. package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +12 -2
  41. package/sdk-core/core/src/worker/activities.rs +276 -10
  42. package/sdk-core/core/src/worker/client/mocks.rs +18 -0
  43. package/sdk-core/core/src/worker/client.rs +16 -3
  44. package/sdk-core/core/src/worker/mod.rs +50 -19
  45. package/sdk-core/core/src/worker/slot_provider.rs +175 -0
  46. package/sdk-core/core/src/worker/workflow/driven_workflow.rs +27 -34
  47. package/sdk-core/core/src/worker/workflow/history_update.rs +4 -1
  48. package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +36 -94
  49. package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +34 -22
  50. package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +50 -34
  51. package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +106 -92
  52. package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +22 -21
  53. package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +386 -499
  54. package/sdk-core/core/src/worker/workflow/machines/mod.rs +12 -2
  55. package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +33 -26
  56. package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +198 -215
  57. package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +66 -62
  58. package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +88 -119
  59. package/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +3 -1
  60. package/sdk-core/core/src/worker/workflow/machines/update_state_machine.rs +411 -0
  61. package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +26 -25
  62. package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +302 -85
  63. package/sdk-core/core/src/worker/workflow/managed_run.rs +179 -132
  64. package/sdk-core/core/src/worker/workflow/mod.rs +121 -46
  65. package/sdk-core/core/src/worker/workflow/run_cache.rs +8 -12
  66. package/sdk-core/core/src/worker/workflow/workflow_stream.rs +45 -38
  67. package/sdk-core/core-api/Cargo.toml +7 -6
  68. package/sdk-core/core-api/src/lib.rs +4 -12
  69. package/sdk-core/core-api/src/telemetry/metrics.rs +334 -0
  70. package/sdk-core/core-api/src/telemetry.rs +53 -42
  71. package/sdk-core/core-api/src/worker.rs +7 -0
  72. package/sdk-core/{.buildkite/docker → docker}/docker-compose.yaml +1 -1
  73. package/sdk-core/etc/dynamic-config.yaml +11 -1
  74. package/sdk-core/fsm/rustfsm_procmacro/Cargo.toml +1 -1
  75. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +1 -3
  76. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +2 -2
  77. package/sdk-core/sdk/Cargo.toml +1 -1
  78. package/sdk-core/sdk/src/lib.rs +85 -7
  79. package/sdk-core/sdk/src/workflow_context/options.rs +4 -0
  80. package/sdk-core/sdk/src/workflow_context.rs +43 -15
  81. package/sdk-core/sdk/src/workflow_future.rs +334 -204
  82. package/sdk-core/sdk-core-protos/Cargo.toml +2 -2
  83. package/sdk-core/sdk-core-protos/build.rs +14 -14
  84. package/sdk-core/sdk-core-protos/protos/api_upstream/.buildkite/Dockerfile +2 -0
  85. package/sdk-core/sdk-core-protos/protos/api_upstream/Makefile +99 -0
  86. package/sdk-core/sdk-core-protos/protos/api_upstream/api-linter.yaml +56 -0
  87. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.gen.yaml +20 -0
  88. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.lock +11 -0
  89. package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +18 -0
  90. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/annotations.proto +31 -0
  91. package/sdk-core/sdk-core-protos/protos/api_upstream/google/api/http.proto +379 -0
  92. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/any.proto +162 -0
  93. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/descriptor.proto +1212 -0
  94. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/duration.proto +115 -0
  95. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/empty.proto +51 -0
  96. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/timestamp.proto +144 -0
  97. package/sdk-core/sdk-core-protos/protos/api_upstream/google/protobuf/wrappers.proto +123 -0
  98. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/batch/v1/message.proto +3 -5
  99. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/command/v1/message.proto +11 -13
  100. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/common/v1/message.proto +2 -4
  101. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
  102. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/reset.proto +1 -1
  103. package/sdk-core/{protos/api_upstream/build/tools.go → sdk-core-protos/protos/api_upstream/temporal/api/export/v1/message.proto} +22 -6
  104. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/filter/v1/message.proto +2 -4
  105. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/history/v1/message.proto +21 -23
  106. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/namespace/v1/message.proto +2 -4
  107. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/request_response.proto +2 -0
  108. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/operatorservice/v1/service.proto +4 -0
  109. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/replication/v1/message.proto +1 -3
  110. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/schedule/v1/message.proto +36 -20
  111. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +13 -0
  112. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/taskqueue/v1/message.proto +2 -4
  113. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/update/v1/message.proto +1 -1
  114. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/version/v1/message.proto +2 -3
  115. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflow/v1/message.proto +18 -20
  116. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/request_response.proto +84 -32
  117. package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/workflowservice/v1/service.proto +205 -47
  118. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +57 -0
  119. package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +27 -0
  120. package/sdk-core/sdk-core-protos/src/history_builder.rs +67 -2
  121. package/sdk-core/sdk-core-protos/src/lib.rs +75 -2
  122. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  123. package/sdk-core/test-utils/Cargo.toml +5 -1
  124. package/sdk-core/test-utils/src/canned_histories.rs +3 -57
  125. package/sdk-core/test-utils/src/interceptors.rs +46 -0
  126. package/sdk-core/test-utils/src/lib.rs +106 -38
  127. package/sdk-core/tests/integ_tests/metrics_tests.rs +110 -15
  128. package/sdk-core/tests/integ_tests/queries_tests.rs +174 -3
  129. package/sdk-core/tests/integ_tests/update_tests.rs +908 -0
  130. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +44 -1
  131. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -1
  132. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +1 -1
  133. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -4
  134. package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +61 -0
  135. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +27 -2
  136. package/sdk-core/tests/integ_tests/workflow_tests.rs +1 -0
  137. package/sdk-core/tests/main.rs +2 -1
  138. package/sdk-core/tests/runner.rs +15 -2
  139. package/src/conversions.rs +75 -89
  140. package/src/helpers.rs +74 -0
  141. package/src/runtime.rs +17 -6
  142. package/src/worker.rs +14 -61
  143. package/ts/index.ts +21 -52
  144. package/sdk-core/.buildkite/docker/Dockerfile +0 -9
  145. package/sdk-core/.buildkite/docker/build.sh +0 -5
  146. package/sdk-core/.buildkite/docker/docker-compose-ci.yaml +0 -27
  147. package/sdk-core/.buildkite/pipeline.yml +0 -57
  148. package/sdk-core/.github/workflows/semgrep.yml +0 -25
  149. package/sdk-core/core/src/worker/workflow/bridge.rs +0 -35
  150. package/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +0 -215
  151. package/sdk-core/protos/api_upstream/.buildkite/Dockerfile +0 -2
  152. package/sdk-core/protos/api_upstream/Makefile +0 -80
  153. package/sdk-core/protos/api_upstream/api-linter.yaml +0 -40
  154. package/sdk-core/protos/api_upstream/buf.yaml +0 -9
  155. package/sdk-core/protos/api_upstream/build/go.mod +0 -7
  156. package/sdk-core/protos/api_upstream/build/go.sum +0 -5
  157. package/sdk-core/protos/api_upstream/go.mod +0 -6
  158. package/sdk-core/protos/testsrv_upstream/dependencies/gogoproto/gogo.proto +0 -141
  159. /package/sdk-core/{.buildkite/docker → docker}/docker-compose-telem.yaml +0 -0
  160. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/docker-compose.yml +0 -0
  161. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.buildkite/pipeline.yml +0 -0
  162. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/CODEOWNERS +0 -0
  163. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  164. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/publish-docs.yml +0 -0
  165. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/.github/workflows/trigger-api-go-update.yml +0 -0
  166. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/LICENSE +0 -0
  167. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/README.md +0 -0
  168. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/batch_operation.proto +0 -0
  169. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/command_type.proto +0 -0
  170. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/common.proto +0 -0
  171. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/event_type.proto +0 -0
  172. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/namespace.proto +0 -0
  173. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/query.proto +0 -0
  174. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/schedule.proto +0 -0
  175. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/task_queue.proto +0 -0
  176. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/update.proto +0 -0
  177. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/enums/v1/workflow.proto +0 -0
  178. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/errordetails/v1/message.proto +0 -0
  179. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/failure/v1/message.proto +0 -0
  180. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/protocol/v1/message.proto +0 -0
  181. /package/sdk-core/{protos → sdk-core-protos/protos}/api_upstream/temporal/api/query/v1/message.proto +0 -0
  182. /package/sdk-core/{protos → sdk-core-protos/protos}/google/rpc/status.proto +0 -0
  183. /package/sdk-core/{protos → sdk-core-protos/protos}/grpc/health/v1/health.proto +0 -0
  184. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_result/activity_result.proto +0 -0
  185. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/activity_task/activity_task.proto +0 -0
  186. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/child_workflow/child_workflow.proto +0 -0
  187. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/common/common.proto +0 -0
  188. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/core_interface.proto +0 -0
  189. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/external_data/external_data.proto +0 -0
  190. /package/sdk-core/{protos → sdk-core-protos/protos}/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +0 -0
  191. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/Makefile +0 -0
  192. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/api-linter.yaml +0 -0
  193. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/buf.yaml +0 -0
  194. /package/sdk-core/{protos/api_upstream → sdk-core-protos/protos/testsrv_upstream}/dependencies/gogoproto/gogo.proto +0 -0
  195. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/request_response.proto +0 -0
  196. /package/sdk-core/{protos → sdk-core-protos/protos}/testsrv_upstream/temporal/api/testservice/v1/service.proto +0 -0
@@ -1,8 +1,8 @@
1
1
  //! Worker-specific client needs
2
2
 
3
3
  pub(crate) mod mocks;
4
-
5
- use temporal_client::{Client, RetryClient, WorkflowService};
4
+ use std::sync::Arc;
5
+ use temporal_client::{Client, RetryClient, SlotManager, WorkflowService};
6
6
  use temporal_sdk_core_protos::{
7
7
  coresdk::workflow_commands::QueryResult,
8
8
  temporal::api::{
@@ -13,6 +13,7 @@ use temporal_sdk_core_protos::{
13
13
  },
14
14
  enums::v1::{TaskQueueKind, WorkflowTaskFailedCause},
15
15
  failure::v1::Failure,
16
+ protocol::v1::Message as ProtocolMessage,
16
17
  query::v1::WorkflowQueryResult,
17
18
  sdk::v1::WorkflowTaskCompletedMetadata,
18
19
  taskqueue::v1::{StickyExecutionAttributes, TaskQueue, TaskQueueMetadata},
@@ -143,6 +144,8 @@ pub(crate) trait WorkerClient: Sync + Send {
143
144
 
144
145
  #[allow(clippy::needless_lifetimes)] // Clippy is wrong here
145
146
  fn capabilities<'a>(&'a self) -> Option<&'a get_system_info_response::Capabilities>;
147
+ fn workers(&self) -> Arc<SlotManager>;
148
+ fn is_mock(&self) -> bool;
146
149
  }
147
150
 
148
151
  #[async_trait::async_trait]
@@ -201,12 +204,12 @@ impl WorkerClient for WorkerClientBag {
201
204
  let request = RespondWorkflowTaskCompletedRequest {
202
205
  task_token: request.task_token.into(),
203
206
  commands: request.commands,
207
+ messages: request.messages,
204
208
  identity: self.identity.clone(),
205
209
  sticky_attributes: request.sticky_attributes,
206
210
  return_new_workflow_task: request.return_new_workflow_task,
207
211
  force_create_new_workflow_task: request.force_create_new_workflow_task,
208
212
  worker_version_stamp: self.worker_version_stamp(),
209
- messages: vec![],
210
213
  binary_checksum: self.binary_checksum(),
211
214
  query_results: request
212
215
  .query_responses
@@ -381,6 +384,14 @@ impl WorkerClient for WorkerClientBag {
381
384
  fn capabilities(&self) -> Option<&Capabilities> {
382
385
  self.client.get_client().inner().capabilities()
383
386
  }
387
+
388
+ fn workers(&self) -> Arc<SlotManager> {
389
+ self.client.get_client().inner().workers()
390
+ }
391
+
392
+ fn is_mock(&self) -> bool {
393
+ false
394
+ }
384
395
  }
385
396
 
386
397
  /// A version of [RespondWorkflowTaskCompletedRequest] that will finish being filled out by the
@@ -391,6 +402,8 @@ pub(crate) struct WorkflowTaskCompletion {
391
402
  pub task_token: TaskToken,
392
403
  /// A list of new commands to send to the server, such as starting a timer.
393
404
  pub commands: Vec<Command>,
405
+ /// A list of protocol messages to send to the server.
406
+ pub messages: Vec<ProtocolMessage>,
394
407
  /// If set, indicate that next task should be queued on sticky queue with given attributes.
395
408
  pub sticky_attributes: Option<StickyExecutionAttributes>,
396
409
  /// Responses to queries in the `queries` field of the workflow task.
@@ -1,5 +1,6 @@
1
1
  mod activities;
2
2
  pub(crate) mod client;
3
+ mod slot_provider;
3
4
  mod workflow;
4
5
 
5
6
  pub use temporal_sdk_core_api::worker::{WorkerConfig, WorkerConfigBuilder};
@@ -12,13 +13,14 @@ pub(crate) use activities::{
12
13
  };
13
14
  pub(crate) use workflow::{wft_poller::new_wft_poller, LEGACY_QUERY_ID};
14
15
 
15
- #[cfg(test)]
16
- pub(crate) use workflow::ManagedWFFunc;
16
+ use temporal_client::WorkerKey;
17
17
 
18
18
  use crate::{
19
- abstractions::MeteredSemaphore,
19
+ abstractions::{dbg_panic, MeteredSemaphore},
20
20
  errors::CompleteWfError,
21
- pollers::{new_activity_task_buffer, new_workflow_task_buffer, WorkflowTaskPoller},
21
+ pollers::{
22
+ new_activity_task_buffer, new_workflow_task_buffer, BoxedActPoller, WorkflowTaskPoller,
23
+ },
22
24
  protosext::validate_activity_completion,
23
25
  telemetry::{
24
26
  metrics::{
@@ -35,6 +37,8 @@ use crate::{
35
37
  ActivityHeartbeat, CompleteActivityError, PollActivityError, PollWfError, WorkerTrait,
36
38
  };
37
39
  use activities::WorkerActivityTasks;
40
+ use futures_util::{stream, StreamExt};
41
+ use slot_provider::SlotProvider;
38
42
  use std::{
39
43
  convert::TryInto,
40
44
  future,
@@ -59,9 +63,9 @@ use temporal_sdk_core_protos::{
59
63
  TaskToken,
60
64
  };
61
65
  use tokio::sync::mpsc::unbounded_channel;
66
+ use tokio_stream::wrappers::UnboundedReceiverStream;
62
67
  use tokio_util::sync::CancellationToken;
63
68
 
64
- use crate::{abstractions::dbg_panic, pollers::BoxedActPoller};
65
69
  #[cfg(test)]
66
70
  use {
67
71
  crate::{
@@ -69,7 +73,6 @@ use {
69
73
  protosext::ValidPollWFTQResponse,
70
74
  },
71
75
  futures::stream::BoxStream,
72
- futures_util::StreamExt,
73
76
  temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollActivityTaskQueueResponse,
74
77
  };
75
78
 
@@ -77,7 +80,8 @@ use {
77
80
  pub struct Worker {
78
81
  config: WorkerConfig,
79
82
  wf_client: Arc<dyn WorkerClient>,
80
-
83
+ /// Registration key to enable eager workflow start for this worker
84
+ worker_key: Option<WorkerKey>,
81
85
  /// Manages all workflows and WFT processing
82
86
  workflows: Workflows,
83
87
  /// Manages activity tasks for this worker/task queue
@@ -98,7 +102,15 @@ pub struct Worker {
98
102
  #[async_trait::async_trait]
99
103
  impl WorkerTrait for Worker {
100
104
  async fn poll_workflow_activation(&self) -> Result<WorkflowActivation, PollWfError> {
101
- self.next_workflow_activation().await
105
+ self.next_workflow_activation().await.map(|mut a| {
106
+ // Attach this worker's Build ID to the activation if appropriate. This is done here
107
+ // to avoid cloning the ID for every workflow instance. Can be lowered when
108
+ // https://github.com/temporalio/sdk-core/issues/567 is done
109
+ if !a.is_replaying {
110
+ a.build_id_for_current_task = self.config.worker_build_id.clone();
111
+ }
112
+ a
113
+ })
102
114
  }
103
115
 
104
116
  #[instrument(skip(self))]
@@ -164,7 +176,11 @@ impl WorkerTrait for Worker {
164
176
  );
165
177
  }
166
178
  self.shutdown_token.cancel();
167
- // First, we want to stop polling of both activity and workflow tasks
179
+ // First, disable Eager Workflow Start
180
+ if let Some(key) = self.worker_key {
181
+ self.wf_client.workers().unregister(key);
182
+ }
183
+ // Second, we want to stop polling of both activity and workflow tasks
168
184
  if let Some(atm) = self.at_task_mgr.as_ref() {
169
185
  atm.initiate_shutdown();
170
186
  }
@@ -194,20 +210,12 @@ impl Worker {
194
210
  telem_instance: Option<&TelemetryInstance>,
195
211
  ) -> Self {
196
212
  info!(task_queue=%config.task_queue, namespace=%config.namespace, "Initializing worker");
197
- let metrics = if let Some(ti) = telem_instance {
198
- MetricsContext::top_level(config.namespace.clone(), ti)
199
- .with_task_q(config.task_queue.clone())
200
- } else {
201
- MetricsContext::no_op()
202
- };
203
- metrics.worker_registered();
204
213
 
205
214
  Self::new_with_pollers(
206
215
  config,
207
216
  sticky_queue_name,
208
217
  client,
209
218
  TaskPollers::Real,
210
- metrics,
211
219
  telem_instance,
212
220
  )
213
221
  }
@@ -223,9 +231,14 @@ impl Worker {
223
231
  sticky_queue_name: Option<String>,
224
232
  client: Arc<dyn WorkerClient>,
225
233
  task_pollers: TaskPollers,
226
- metrics: MetricsContext,
227
234
  telem_instance: Option<&TelemetryInstance>,
228
235
  ) -> Self {
236
+ let metrics = if let Some(ti) = telem_instance {
237
+ MetricsContext::top_level(config.namespace.clone(), config.task_queue.clone(), ti)
238
+ } else {
239
+ MetricsContext::no_op()
240
+ };
241
+ metrics.worker_registered();
229
242
  let shutdown_token = CancellationToken::new();
230
243
  let wft_semaphore = Arc::new(MeteredSemaphore::new(
231
244
  config.max_outstanding_workflow_tasks,
@@ -237,7 +250,7 @@ impl Worker {
237
250
  metrics.with_new_attrs([activity_worker_type()]),
238
251
  MetricsContext::available_task_slots,
239
252
  ));
240
-
253
+ let (external_wft_tx, external_wft_rx) = unbounded_channel();
241
254
  let (wft_stream, act_poller) = match task_pollers {
242
255
  TaskPollers::Real => {
243
256
  let max_nonsticky_polls = if sticky_queue_name.is_some() {
@@ -299,6 +312,15 @@ impl Worker {
299
312
  sticky_queue_poller,
300
313
  ));
301
314
  let wft_stream = new_wft_poller(wf_task_poll_buffer, metrics.clone());
315
+ let wft_stream = if !client.is_mock() {
316
+ // Some replay tests combine a mock client with real pollers,
317
+ // and they don't need to use the external stream
318
+ stream::select(wft_stream, UnboundedReceiverStream::new(external_wft_rx))
319
+ .left_stream()
320
+ } else {
321
+ wft_stream.right_stream()
322
+ };
323
+
302
324
  #[cfg(test)]
303
325
  let wft_stream = wft_stream.left_stream();
304
326
  (wft_stream, act_poll_buffer)
@@ -342,6 +364,7 @@ impl Worker {
342
364
  config.max_heartbeat_throttle_interval,
343
365
  config.default_heartbeat_throttle_interval,
344
366
  config.graceful_shutdown_period,
367
+ config.local_timeout_buffer_for_activities,
345
368
  )
346
369
  });
347
370
  let poll_on_non_local_activities = at_task_mgr.is_some();
@@ -349,7 +372,15 @@ impl Worker {
349
372
  info!("Activity polling is disabled for this worker");
350
373
  };
351
374
  let la_sink = LAReqSink::new(local_act_mgr.clone(), config.wf_state_inputs.clone());
375
+ let provider = SlotProvider::new(
376
+ config.namespace.clone(),
377
+ config.task_queue.clone(),
378
+ wft_semaphore.clone(),
379
+ external_wft_tx,
380
+ );
381
+ let worker_key = client.workers().register(Box::new(provider));
352
382
  Self {
383
+ worker_key,
353
384
  wf_client: client.clone(),
354
385
  workflows: Workflows::new(
355
386
  build_wf_basics(
@@ -0,0 +1,175 @@
1
+ //! This module implements traits defined in the client to dispatch a
2
+ //! WFT to a worker bypassing the server.
3
+ //! This enables latency optimizations such as Eager Workflow Start.
4
+
5
+ use crate::{
6
+ abstractions::{MeteredSemaphore, OwnedMeteredSemPermit},
7
+ protosext::ValidPollWFTQResponse,
8
+ worker::workflow::wft_poller::validate_wft,
9
+ };
10
+
11
+ use std::sync::Arc;
12
+ use temporal_client::{Slot as SlotTrait, SlotProvider as SlotProviderTrait};
13
+ use temporal_sdk_core_protos::temporal::api::workflowservice::v1::PollWorkflowTaskQueueResponse;
14
+ use tokio::sync::mpsc::UnboundedSender;
15
+ use tonic::Status;
16
+
17
+ type WFTStreamSender =
18
+ UnboundedSender<Result<(ValidPollWFTQResponse, OwnedMeteredSemPermit), Status>>;
19
+
20
+ pub struct Slot {
21
+ permit: OwnedMeteredSemPermit,
22
+ external_wft_tx: WFTStreamSender,
23
+ }
24
+
25
+ impl Slot {
26
+ fn new(permit: OwnedMeteredSemPermit, external_wft_tx: WFTStreamSender) -> Self {
27
+ Self {
28
+ permit,
29
+ external_wft_tx,
30
+ }
31
+ }
32
+ }
33
+
34
+ impl SlotTrait for Slot {
35
+ fn schedule_wft(
36
+ self: Box<Self>,
37
+ task: PollWorkflowTaskQueueResponse,
38
+ ) -> Result<(), anyhow::Error> {
39
+ let wft = validate_wft(task)?;
40
+ self.external_wft_tx.send(Ok((wft, self.permit)))?;
41
+ Ok(())
42
+ }
43
+ }
44
+
45
+ #[derive(derive_more::DebugCustom)]
46
+ #[debug(fmt = "SlotProvider {{ namespace:{namespace}, task_queue: {task_queue} }}")]
47
+ pub struct SlotProvider {
48
+ namespace: String,
49
+ task_queue: String,
50
+ wft_semaphore: Arc<MeteredSemaphore>,
51
+ external_wft_tx: WFTStreamSender,
52
+ }
53
+
54
+ impl SlotProvider {
55
+ pub(crate) fn new(
56
+ namespace: String,
57
+ task_queue: String,
58
+ wft_semaphore: Arc<MeteredSemaphore>,
59
+ external_wft_tx: WFTStreamSender,
60
+ ) -> Self {
61
+ Self {
62
+ namespace,
63
+ task_queue,
64
+ wft_semaphore,
65
+ external_wft_tx,
66
+ }
67
+ }
68
+ }
69
+
70
+ impl SlotProviderTrait for SlotProvider {
71
+ fn namespace(&self) -> &str {
72
+ &self.namespace
73
+ }
74
+ fn task_queue(&self) -> &str {
75
+ &self.task_queue
76
+ }
77
+ fn try_reserve_wft_slot(&self) -> Option<Box<dyn SlotTrait + Send>> {
78
+ match self.wft_semaphore.try_acquire_owned().ok() {
79
+ Some(permit) => Some(Box::new(Slot::new(permit, self.external_wft_tx.clone()))),
80
+ None => None,
81
+ }
82
+ }
83
+ }
84
+
85
+ #[cfg(test)]
86
+ mod tests {
87
+ use super::*;
88
+
89
+ use temporal_sdk_core_protos::temporal::api::{
90
+ common::v1::{WorkflowExecution, WorkflowType},
91
+ history::v1::History,
92
+ taskqueue::v1::TaskQueue,
93
+ };
94
+ use tokio::sync::mpsc::unbounded_channel;
95
+
96
+ // make validate_wft() happy
97
+ fn new_validatable_response() -> PollWorkflowTaskQueueResponse {
98
+ PollWorkflowTaskQueueResponse {
99
+ workflow_execution_task_queue: Some(TaskQueue::default()),
100
+ workflow_execution: Some(WorkflowExecution::default()),
101
+ workflow_type: Some(WorkflowType::default()),
102
+ history: Some(History::default()),
103
+ ..Default::default()
104
+ }
105
+ }
106
+
107
+ #[tokio::test]
108
+ async fn slot_propagates_through_channel() {
109
+ let wft_semaphore = Arc::new(MeteredSemaphore::new(
110
+ 2,
111
+ crate::MetricsContext::no_op(),
112
+ |_, _| {},
113
+ ));
114
+ let (external_wft_tx, mut external_wft_rx) = unbounded_channel();
115
+
116
+ let provider = SlotProvider::new(
117
+ "my_namespace".to_string(),
118
+ "my_queue".to_string(),
119
+ wft_semaphore,
120
+ external_wft_tx,
121
+ );
122
+
123
+ let slot = provider
124
+ .try_reserve_wft_slot()
125
+ .expect("failed to reserver slot");
126
+ let p = slot.schedule_wft(new_validatable_response());
127
+ assert!(p.is_ok());
128
+ assert!(external_wft_rx.recv().await.is_some());
129
+ }
130
+
131
+ #[tokio::test]
132
+ async fn channel_closes_when_provider_drops() {
133
+ let (external_wft_tx, mut external_wft_rx) = unbounded_channel();
134
+ {
135
+ let external_wft_tx = external_wft_tx;
136
+ let wft_semaphore = Arc::new(MeteredSemaphore::new(
137
+ 2,
138
+ crate::MetricsContext::no_op(),
139
+ |_, _| {},
140
+ ));
141
+ let provider = SlotProvider::new(
142
+ "my_namespace".to_string(),
143
+ "my_queue".to_string(),
144
+ wft_semaphore,
145
+ external_wft_tx,
146
+ );
147
+ assert!(provider.try_reserve_wft_slot().is_some());
148
+ }
149
+ assert!(external_wft_rx.recv().await.is_none());
150
+ }
151
+
152
+ #[tokio::test]
153
+ async fn unused_slots_reclaimed() {
154
+ let wft_semaphore = Arc::new(MeteredSemaphore::new(
155
+ 2,
156
+ crate::MetricsContext::no_op(),
157
+ |_, _| {},
158
+ ));
159
+ {
160
+ let wft_semaphore = wft_semaphore.clone();
161
+ let (external_wft_tx, _) = unbounded_channel();
162
+ let provider = SlotProvider::new(
163
+ "my_namespace".to_string(),
164
+ "my_queue".to_string(),
165
+ wft_semaphore.clone(),
166
+ external_wft_tx,
167
+ );
168
+ let slot = provider.try_reserve_wft_slot();
169
+ assert!(slot.is_some());
170
+ assert_eq!(wft_semaphore.available_permits(), 1);
171
+ // drop slot without using it
172
+ }
173
+ assert_eq!(wft_semaphore.available_permits(), 2);
174
+ }
175
+ }
@@ -1,34 +1,36 @@
1
- use crate::worker::workflow::{OutgoingJob, WFCommand, WorkflowStartedInfo};
1
+ use crate::{
2
+ telemetry::VecDisplayer,
3
+ worker::workflow::{OutgoingJob, WFCommand, WorkflowStartedInfo},
4
+ };
2
5
  use prost_types::Timestamp;
6
+ use std::sync::mpsc::{self, Receiver, Sender};
3
7
  use temporal_sdk_core_protos::{
4
8
  coresdk::workflow_activation::{start_workflow_from_attribs, WorkflowActivationJob},
5
9
  temporal::api::history::v1::WorkflowExecutionStartedEventAttributes,
6
10
  utilities::TryIntoOrNone,
7
11
  };
8
12
 
9
- /// Abstracts away the concept of an actual workflow implementation, handling sending it new
10
- /// jobs and fetching output from it.
13
+ /// Represents a connection to a lang side workflow that can have activations fed into it and
14
+ /// command responses pulled out.
11
15
  pub struct DrivenWorkflow {
12
16
  started_attrs: Option<WorkflowStartedInfo>,
13
- fetcher: Box<dyn WorkflowFetcher>,
17
+ incoming_commands: Receiver<Vec<WFCommand>>,
14
18
  /// Outgoing activation jobs that need to be sent to the lang sdk
15
19
  outgoing_wf_activation_jobs: Vec<OutgoingJob>,
16
20
  }
17
21
 
18
- impl<WF> From<Box<WF>> for DrivenWorkflow
19
- where
20
- WF: WorkflowFetcher + 'static,
21
- {
22
- fn from(wf: Box<WF>) -> Self {
23
- Self {
24
- started_attrs: None,
25
- fetcher: wf,
26
- outgoing_wf_activation_jobs: Default::default(),
27
- }
28
- }
29
- }
30
-
31
22
  impl DrivenWorkflow {
23
+ pub fn new() -> (Self, Sender<Vec<WFCommand>>) {
24
+ let (tx, rx) = mpsc::channel();
25
+ (
26
+ Self {
27
+ started_attrs: None,
28
+ incoming_commands: rx,
29
+ outgoing_wf_activation_jobs: vec![],
30
+ },
31
+ tx,
32
+ )
33
+ }
32
34
  /// Start the workflow
33
35
  pub fn start(
34
36
  &mut self,
@@ -72,24 +74,15 @@ impl DrivenWorkflow {
72
74
  .map(Into::into)
73
75
  .collect()
74
76
  }
75
- }
76
77
 
77
- impl WorkflowFetcher for DrivenWorkflow {
78
- fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
79
- self.fetcher.fetch_workflow_iteration_output()
80
- }
81
- }
82
-
83
- /// Implementors of this trait represent a way to fetch output from executing/iterating some
84
- /// workflow code (or a mocked workflow).
85
- pub trait WorkflowFetcher: Send {
86
78
  /// Obtain any output from the workflow's recent execution(s). Because the lang sdk is
87
79
  /// responsible for calling workflow code as a result of receiving tasks from
88
- /// [crate::Core::poll_task], we cannot directly iterate it here. Thus implementations of this
89
- /// trait are expected to either buffer output or otherwise produce it on demand when this
90
- /// function is called.
91
- ///
92
- /// In the case of the real [WorkflowBridge] implementation, commands are simply pulled from
93
- /// a buffer that the language side sinks into when it calls [crate::Core::complete_task]
94
- fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand>;
80
+ /// [crate::Core::poll_task], we cannot directly iterate it here. Commands are simply pulled
81
+ /// from a buffer that the language side sinks into when it calls [crate::Core::complete_task]
82
+ pub fn fetch_workflow_iteration_output(&mut self) -> Vec<WFCommand> {
83
+ let in_cmds = self.incoming_commands.try_recv();
84
+ let in_cmds = in_cmds.unwrap_or_else(|_| vec![WFCommand::NoCommandsFromLang]);
85
+ debug!(in_cmds = %in_cmds.display(), "wf bridge iteration fetch");
86
+ in_cmds
87
+ }
95
88
  }
@@ -55,8 +55,10 @@ impl Debug for HistoryUpdate {
55
55
  if self.is_real() {
56
56
  write!(
57
57
  f,
58
- "HistoryUpdate(previous_started_event_id: {}, length: {}, first_event_id: {:?})",
58
+ "HistoryUpdate(previous_started_event_id: {}, started_id: {}, \
59
+ length: {}, first_event_id: {:?})",
59
60
  self.previous_wft_started_id,
61
+ self.wft_started_id,
60
62
  self.events.len(),
61
63
  self.events.first().map(|e| e.event_id)
62
64
  )
@@ -164,6 +166,7 @@ impl HistoryPaginator {
164
166
  legacy_query: wft.legacy_query,
165
167
  query_requests: wft.query_requests,
166
168
  update,
169
+ messages: wft.messages,
167
170
  };
168
171
  Ok((paginator, prepared))
169
172
  }
@@ -804,87 +804,17 @@ fn convert_payloads(
804
804
  mod test {
805
805
  use super::*;
806
806
  use crate::{
807
- internal_flags::InternalFlags, replay::TestHistoryBuilder, test_help::canned_histories,
808
- worker::workflow::ManagedWFFunc,
807
+ internal_flags::InternalFlags,
808
+ replay::TestHistoryBuilder,
809
+ test_help::{build_fake_sdk, MockPollCfg, ResponseType},
809
810
  };
810
- use rstest::{fixture, rstest};
811
811
  use std::{cell::RefCell, mem::discriminant, rc::Rc};
812
- use temporal_sdk::{
813
- ActivityOptions, CancellableFuture, WfContext, WorkflowFunction, WorkflowResult,
814
- };
812
+ use temporal_sdk::{ActivityOptions, CancellableFuture, WfContext, WorkflowFunction};
815
813
  use temporal_sdk_core_protos::{
816
814
  coresdk::workflow_activation::{workflow_activation_job, WorkflowActivationJob},
817
- DEFAULT_ACTIVITY_TYPE,
815
+ DEFAULT_WORKFLOW_TYPE,
818
816
  };
819
-
820
- #[fixture]
821
- fn activity_happy_hist() -> ManagedWFFunc {
822
- let func = WorkflowFunction::new(activity_wf);
823
- let t = canned_histories::single_activity("activity-id-1");
824
- assert_eq!(2, t.get_full_history_info().unwrap().wf_task_count());
825
- ManagedWFFunc::new(t, func, vec![])
826
- }
827
-
828
- #[fixture]
829
- fn activity_failure_hist() -> ManagedWFFunc {
830
- let func = WorkflowFunction::new(activity_wf);
831
- let t = canned_histories::single_failed_activity("activity-id-1");
832
- assert_eq!(2, t.get_full_history_info().unwrap().wf_task_count());
833
- ManagedWFFunc::new(t, func, vec![])
834
- }
835
-
836
- async fn activity_wf(command_sink: WfContext) -> WorkflowResult<()> {
837
- command_sink
838
- .activity(ActivityOptions {
839
- activity_id: Some("activity-id-1".to_string()),
840
- activity_type: DEFAULT_ACTIVITY_TYPE.to_string(),
841
- ..Default::default()
842
- })
843
- .await;
844
- Ok(().into())
845
- }
846
-
847
- #[rstest(
848
- wfm,
849
- case::success(activity_happy_hist()),
850
- case::failure(activity_failure_hist())
851
- )]
852
- #[tokio::test]
853
- async fn single_activity_inc(mut wfm: ManagedWFFunc) {
854
- wfm.get_next_activation().await.unwrap();
855
- let commands = wfm.get_server_commands().commands;
856
- assert_eq!(commands.len(), 1);
857
- assert_eq!(
858
- commands[0].command_type,
859
- CommandType::ScheduleActivityTask as i32
860
- );
861
-
862
- wfm.get_next_activation().await.unwrap();
863
- let commands = wfm.get_server_commands().commands;
864
- assert_eq!(commands.len(), 1);
865
- assert_eq!(
866
- commands[0].command_type,
867
- CommandType::CompleteWorkflowExecution as i32
868
- );
869
- wfm.shutdown().await.unwrap();
870
- }
871
-
872
- #[rstest(
873
- wfm,
874
- case::success(activity_happy_hist()),
875
- case::failure(activity_failure_hist())
876
- )]
877
- #[tokio::test]
878
- async fn single_activity_full(mut wfm: ManagedWFFunc) {
879
- wfm.process_all_activations().await.unwrap();
880
- let commands = wfm.get_server_commands().commands;
881
- assert_eq!(commands.len(), 1);
882
- assert_eq!(
883
- commands[0].command_type,
884
- CommandType::CompleteWorkflowExecution as i32
885
- );
886
- wfm.shutdown().await.unwrap();
887
- }
817
+ use temporal_sdk_core_test_utils::interceptors::ActivationAssertionsInterceptor;
888
818
 
889
819
  #[tokio::test]
890
820
  async fn immediate_activity_cancelation() {
@@ -900,24 +830,36 @@ mod test {
900
830
  t.add_by_type(EventType::WorkflowExecutionStarted);
901
831
  t.add_full_wf_task();
902
832
  t.add_workflow_execution_completed();
903
- let mut wfm = ManagedWFFunc::new(t, func, vec![]);
904
-
905
- let activation = wfm.process_all_activations().await.unwrap();
906
- wfm.get_server_commands();
907
- assert_matches!(
908
- activation.jobs.as_slice(),
909
- [WorkflowActivationJob {
910
- variant: Some(workflow_activation_job::Variant::ResolveActivity(
911
- ResolveActivity {
912
- result: Some(ActivityResolution {
913
- status: Some(activity_resolution::Status::Cancelled(_))
914
- }),
915
- ..
916
- }
917
- )),
918
- },]
919
- );
920
- wfm.shutdown().await.unwrap();
833
+ let mut worker = build_fake_sdk(MockPollCfg::from_resps(t, [ResponseType::AllHistory]));
834
+ worker.register_wf(DEFAULT_WORKFLOW_TYPE, func);
835
+
836
+ let mut aai = ActivationAssertionsInterceptor::default();
837
+ aai.then(|a| {
838
+ assert_matches!(
839
+ a.jobs.as_slice(),
840
+ [WorkflowActivationJob {
841
+ variant: Some(workflow_activation_job::Variant::StartWorkflow(_)),
842
+ }]
843
+ )
844
+ });
845
+ aai.then(|a| {
846
+ assert_matches!(
847
+ a.jobs.as_slice(),
848
+ [WorkflowActivationJob {
849
+ variant: Some(workflow_activation_job::Variant::ResolveActivity(
850
+ ResolveActivity {
851
+ result: Some(ActivityResolution {
852
+ status: Some(activity_resolution::Status::Cancelled(_))
853
+ }),
854
+ ..
855
+ }
856
+ )),
857
+ },]
858
+ )
859
+ });
860
+
861
+ worker.set_worker_interceptor(aai);
862
+ worker.run().await.unwrap();
921
863
  }
922
864
 
923
865
  #[test]