@temporalio/core-bridge 0.17.2 → 0.18.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 (170) hide show
  1. package/Cargo.lock +339 -226
  2. package/Cargo.toml +7 -3
  3. package/common.js +50 -0
  4. package/index.d.ts +7 -0
  5. package/index.js +12 -0
  6. package/package.json +7 -4
  7. package/releases/aarch64-apple-darwin/index.node +0 -0
  8. package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
  9. package/{index.node → releases/index.node} +0 -0
  10. package/releases/x86_64-apple-darwin/index.node +0 -0
  11. package/releases/x86_64-pc-windows-msvc/index.node +0 -0
  12. package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
  13. package/scripts/build.js +10 -50
  14. package/sdk-core/.buildkite/docker/Dockerfile +1 -1
  15. package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
  16. package/sdk-core/.buildkite/pipeline.yml +2 -0
  17. package/sdk-core/Cargo.toml +1 -88
  18. package/sdk-core/README.md +30 -6
  19. package/sdk-core/bridge-ffi/Cargo.toml +24 -0
  20. package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
  21. package/sdk-core/bridge-ffi/build.rs +25 -0
  22. package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
  23. package/sdk-core/bridge-ffi/src/lib.rs +829 -0
  24. package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
  25. package/sdk-core/client/Cargo.toml +32 -0
  26. package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
  27. package/sdk-core/client/src/metrics.rs +89 -0
  28. package/sdk-core/client/src/mocks.rs +167 -0
  29. package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
  30. package/sdk-core/core/Cargo.toml +96 -0
  31. package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
  32. package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
  33. package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
  34. package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
  35. package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
  36. package/sdk-core/{src → core/src}/core_tests/queries.rs +45 -52
  37. package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
  38. package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
  39. package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
  40. package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +264 -286
  41. package/sdk-core/core/src/lib.rs +374 -0
  42. package/sdk-core/{src → core/src}/log_export.rs +3 -27
  43. package/sdk-core/core/src/pending_activations.rs +162 -0
  44. package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
  45. package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
  46. package/sdk-core/core/src/protosext/mod.rs +396 -0
  47. package/sdk-core/core/src/replay/mod.rs +210 -0
  48. package/sdk-core/core/src/retry_logic.rs +144 -0
  49. package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
  50. package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
  51. package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
  52. package/sdk-core/{src → core/src}/test_help/mod.rs +34 -73
  53. package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
  54. package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
  55. package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
  56. package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
  57. package/sdk-core/{src → core/src}/worker/mod.rs +305 -195
  58. package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
  59. package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
  60. package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
  61. package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
  62. package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
  63. package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
  64. package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
  65. package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +19 -21
  66. package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
  67. package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
  68. package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
  69. package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
  70. package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
  71. package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
  72. package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
  73. package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
  74. package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
  75. package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
  76. package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
  77. package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
  78. package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
  79. package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +344 -160
  80. package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
  81. package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
  82. package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
  83. package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
  84. package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +297 -81
  85. package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
  86. package/sdk-core/{src → core-api/src}/errors.rs +42 -90
  87. package/sdk-core/core-api/src/lib.rs +158 -0
  88. package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
  89. package/sdk-core/etc/deps.svg +156 -0
  90. package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
  91. package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
  92. package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
  93. package/sdk-core/histories/fail_wf_task.bin +0 -0
  94. package/sdk-core/histories/timer_workflow_history.bin +0 -0
  95. package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
  96. package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
  97. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
  98. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
  99. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
  100. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
  101. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
  102. package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
  103. package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
  104. package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
  105. package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
  106. package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
  107. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
  108. package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
  109. package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
  110. package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
  111. package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
  112. package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
  113. package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
  114. package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
  115. package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
  116. package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
  117. package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
  118. package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
  119. package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
  120. package/sdk-core/sdk/Cargo.toml +32 -0
  121. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
  122. package/sdk-core/sdk/src/lib.rs +699 -0
  123. package/sdk-core/sdk/src/payload_converter.rs +11 -0
  124. package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
  125. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
  126. package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
  127. package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
  128. package/sdk-core/sdk-core-protos/build.rs +28 -6
  129. package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
  130. package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
  131. package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
  132. package/sdk-core/sdk-core-protos/src/lib.rs +594 -168
  133. package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
  134. package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
  135. package/sdk-core/test-utils/Cargo.toml +32 -0
  136. package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
  137. package/sdk-core/test-utils/src/histfetch.rs +28 -0
  138. package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
  139. package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
  140. package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
  141. package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
  142. package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
  143. package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
  144. package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
  145. package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
  146. package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
  147. package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
  148. package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
  149. package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
  150. package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
  151. package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
  152. package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
  153. package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
  154. package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
  155. package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
  156. package/sdk-core/tests/load_tests.rs +6 -6
  157. package/sdk-core/tests/main.rs +2 -2
  158. package/src/conversions.rs +24 -21
  159. package/src/errors.rs +8 -0
  160. package/src/lib.rs +323 -211
  161. package/sdk-core/protos/local/activity_result.proto +0 -46
  162. package/sdk-core/protos/local/activity_task.proto +0 -66
  163. package/sdk-core/src/core_tests/retry.rs +0 -147
  164. package/sdk-core/src/lib.rs +0 -403
  165. package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
  166. package/sdk-core/src/pending_activations.rs +0 -249
  167. package/sdk-core/src/protosext/mod.rs +0 -160
  168. package/sdk-core/src/prototype_rust_sdk.rs +0 -412
  169. package/sdk-core/src/task_token.rs +0 -20
  170. package/sdk-core/src/test_help/history_info.rs +0 -158
@@ -0,0 +1,374 @@
1
+ #![warn(missing_docs)] // error if there are missing docs
2
+ #![allow(clippy::upper_case_acronyms)]
3
+
4
+ //! This crate provides a basis for creating new Temporal SDKs without completely starting from
5
+ //! scratch
6
+
7
+ #[cfg(test)]
8
+ #[macro_use]
9
+ pub extern crate assert_matches;
10
+ #[macro_use]
11
+ extern crate tracing;
12
+
13
+ mod log_export;
14
+ mod pending_activations;
15
+ mod pollers;
16
+ mod protosext;
17
+ pub mod replay;
18
+ pub(crate) mod retry_logic;
19
+ pub(crate) mod telemetry;
20
+ mod worker;
21
+ mod workflow;
22
+
23
+ #[cfg(test)]
24
+ mod core_tests;
25
+ #[cfg(test)]
26
+ #[macro_use]
27
+ mod test_help;
28
+
29
+ pub(crate) use temporal_sdk_core_api::errors;
30
+
31
+ pub use pollers::{
32
+ ClientTlsConfig, RetryConfig, RetryGateway, ServerGateway, ServerGatewayApis,
33
+ ServerGatewayOptions, ServerGatewayOptionsBuilder, TlsConfig,
34
+ };
35
+ pub use telemetry::{TelemetryOptions, TelemetryOptionsBuilder};
36
+ pub use temporal_sdk_core_api as api;
37
+ pub use temporal_sdk_core_protos as protos;
38
+ pub use temporal_sdk_core_protos::TaskToken;
39
+ pub use url::Url;
40
+ pub use worker::{WorkerConfig, WorkerConfigBuilder};
41
+
42
+ use crate::{
43
+ telemetry::{
44
+ fetch_global_buffered_logs,
45
+ metrics::{MetricsContext, METRIC_METER},
46
+ telemetry_init,
47
+ },
48
+ worker::{Worker, WorkerDispatcher},
49
+ };
50
+ use std::{
51
+ ops::Deref,
52
+ sync::{
53
+ atomic::{AtomicBool, Ordering},
54
+ Arc,
55
+ },
56
+ };
57
+ use temporal_sdk_core_api::{
58
+ errors::{
59
+ CompleteActivityError, CompleteWfError, CoreInitError, PollActivityError, PollWfError,
60
+ WorkerRegistrationError,
61
+ },
62
+ Core, CoreLog,
63
+ };
64
+ use temporal_sdk_core_protos::{
65
+ coresdk::{
66
+ activity_task::ActivityTask,
67
+ workflow_activation::{remove_from_cache::EvictionReason, WorkflowActivation},
68
+ workflow_completion::WorkflowActivationCompletion,
69
+ ActivityHeartbeat, ActivityTaskCompletion,
70
+ },
71
+ temporal::api::history::v1::History,
72
+ };
73
+
74
+ #[cfg(test)]
75
+ use crate::test_help::MockWorker;
76
+
77
+ lazy_static::lazy_static! {
78
+ /// A process-wide unique string, which will be different on every startup
79
+ static ref PROCCESS_UNIQ_ID: String = {
80
+ uuid::Uuid::new_v4().to_simple().to_string()
81
+ };
82
+ }
83
+
84
+ /// Holds various configuration information required to call [init]
85
+ #[derive(Debug, Clone, derive_builder::Builder)]
86
+ #[builder(setter(into))]
87
+ #[non_exhaustive]
88
+ pub struct CoreInitOptions {
89
+ /// Options for the connection to the temporal server
90
+ pub gateway_opts: ServerGatewayOptions,
91
+ /// Options for telemetry (traces and metrics)
92
+ #[builder(default)]
93
+ pub telemetry_opts: TelemetryOptions,
94
+ }
95
+
96
+ /// Initializes an instance of the core sdk and establishes a connection to the temporal server.
97
+ /// Expects that a tokio runtime exists.
98
+ pub async fn init(opts: CoreInitOptions) -> Result<CoreSDK, CoreInitError> {
99
+ telemetry_init(&opts.telemetry_opts).map_err(CoreInitError::TelemetryInitError)?;
100
+ // Initialize server client
101
+ let server_gateway = opts.gateway_opts.connect(Some(&METRIC_METER)).await?;
102
+
103
+ Ok(CoreSDK::new(server_gateway, opts))
104
+ }
105
+
106
+ /// Initialize core using a provided gateway instance, which is typically a mock
107
+ pub fn init_mock_gateway<SG: ServerGatewayApis + Send + Sync + 'static>(
108
+ opts: CoreInitOptions,
109
+ server_gateway: SG,
110
+ ) -> Result<CoreSDK, CoreInitError> {
111
+ telemetry_init(&opts.telemetry_opts).map_err(CoreInitError::TelemetryInitError)?;
112
+ Ok(CoreSDK::new(server_gateway, opts))
113
+ }
114
+
115
+ /// Implements the [Core] trait
116
+ pub struct CoreSDK {
117
+ /// Options provided at initialization time
118
+ init_options: CoreInitOptions,
119
+ /// A client for interacting with the Temporal service.
120
+ server_gateway: Arc<dyn ServerGatewayApis + Send + Sync>,
121
+ /// Controls access to workers
122
+ workers: WorkerDispatcher,
123
+ /// Has shutdown been called and all workers drained of tasks?
124
+ whole_core_shutdown: AtomicBool,
125
+ /// Top-level metrics context
126
+ metrics: MetricsContext,
127
+ }
128
+
129
+ #[async_trait::async_trait]
130
+ impl Core for CoreSDK {
131
+ fn register_worker(&self, config: WorkerConfig) -> Result<(), WorkerRegistrationError> {
132
+ info!(
133
+ task_queue = config.task_queue.as_str(),
134
+ "Registering worker"
135
+ );
136
+ let sticky_q = self.get_sticky_q_name_for_worker(&config);
137
+ self.workers.new_worker(
138
+ config,
139
+ sticky_q,
140
+ self.server_gateway.clone(),
141
+ self.metrics.clone(),
142
+ )
143
+ }
144
+
145
+ #[instrument(level = "debug", skip(self), fields(run_id))]
146
+ async fn poll_workflow_activation(
147
+ &self,
148
+ task_queue: &str,
149
+ ) -> Result<WorkflowActivation, PollWfError> {
150
+ let worker = self.worker(task_queue)?;
151
+ worker.next_workflow_activation().await
152
+ }
153
+
154
+ #[instrument(level = "debug", skip(self))]
155
+ async fn poll_activity_task(
156
+ &self,
157
+ task_queue: &str,
158
+ ) -> Result<ActivityTask, PollActivityError> {
159
+ loop {
160
+ if self.whole_core_shutdown.load(Ordering::Relaxed) {
161
+ return Err(PollActivityError::ShutDown);
162
+ }
163
+ let worker = self.worker(task_queue)?;
164
+ match worker.activity_poll().await.transpose() {
165
+ Some(r) => break r,
166
+ None => {
167
+ tokio::task::yield_now().await;
168
+ continue;
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ #[instrument(level = "debug", skip(self, completion),
175
+ fields(completion=%&completion, run_id=%completion.run_id))]
176
+ async fn complete_workflow_activation(
177
+ &self,
178
+ completion: WorkflowActivationCompletion,
179
+ ) -> Result<(), CompleteWfError> {
180
+ let worker = self.worker(&completion.task_queue)?;
181
+ worker.complete_workflow_activation(completion).await
182
+ }
183
+
184
+ #[instrument(level = "debug", skip(self))]
185
+ async fn complete_activity_task(
186
+ &self,
187
+ completion: ActivityTaskCompletion,
188
+ ) -> Result<(), CompleteActivityError> {
189
+ let task_token = TaskToken(completion.task_token);
190
+ let status = if let Some(s) = completion.result.and_then(|r| r.status) {
191
+ s
192
+ } else {
193
+ return Err(CompleteActivityError::MalformedActivityCompletion {
194
+ reason: "Activity completion had empty result/status field".to_owned(),
195
+ completion: None,
196
+ });
197
+ };
198
+
199
+ let worker = self.worker(&completion.task_queue)?;
200
+ worker.complete_activity(task_token, status).await
201
+ }
202
+
203
+ fn record_activity_heartbeat(&self, details: ActivityHeartbeat) {
204
+ if let Ok(w) = self.worker(&details.task_queue) {
205
+ w.record_heartbeat(details);
206
+ }
207
+ }
208
+
209
+ fn request_workflow_eviction(&self, task_queue: &str, run_id: &str) {
210
+ if let Ok(w) = self.worker(task_queue) {
211
+ w.request_wf_eviction(
212
+ run_id,
213
+ "Eviction explicitly requested by lang",
214
+ EvictionReason::LangRequested,
215
+ );
216
+ }
217
+ }
218
+
219
+ fn server_gateway(&self) -> Arc<dyn ServerGatewayApis + Send + Sync> {
220
+ self.server_gateway.clone()
221
+ }
222
+
223
+ async fn shutdown(&self) {
224
+ self.workers.shutdown_all().await;
225
+ self.whole_core_shutdown.store(true, Ordering::Relaxed);
226
+ }
227
+
228
+ async fn shutdown_worker(&self, task_queue: &str) {
229
+ self.workers.shutdown_one(task_queue).await;
230
+ }
231
+
232
+ fn fetch_buffered_logs(&self) -> Vec<CoreLog> {
233
+ fetch_global_buffered_logs()
234
+ }
235
+ }
236
+
237
+ impl CoreSDK {
238
+ pub(crate) fn new<SG: ServerGatewayApis + Send + Sync + 'static>(
239
+ server_gateway: SG,
240
+ init_options: CoreInitOptions,
241
+ ) -> Self {
242
+ let server_gateway = Arc::new(server_gateway);
243
+ let workers = WorkerDispatcher::default();
244
+ Self {
245
+ workers,
246
+ init_options,
247
+ whole_core_shutdown: AtomicBool::new(false),
248
+ metrics: MetricsContext::top_level(server_gateway.get_options().namespace.clone()),
249
+ server_gateway,
250
+ }
251
+ }
252
+
253
+ /// Register a worker for replaying a specific history. The worker should use a unique task
254
+ /// queue name. It will auto-shutdown as soon as the history has finished being replayed. The
255
+ /// provided gateway should be a mock, and this should only be used for workflow testing
256
+ /// purposes.
257
+ pub fn register_replay_worker(
258
+ &self,
259
+ config: WorkerConfig,
260
+ gateway: Arc<dyn ServerGatewayApis + Send + Sync>,
261
+ history: &History,
262
+ ) -> Result<(), anyhow::Error> {
263
+ info!(
264
+ task_queue = config.task_queue.as_str(),
265
+ "Registering replay worker"
266
+ );
267
+ // Could possibly just use mocked pollers here, but they'd need to be un-test-moded
268
+ let run_id = history.extract_run_id_from_start()?.to_string();
269
+ let last_event = history.last_event_id();
270
+ let tq = config.task_queue.clone();
271
+ let mut worker = Worker::new(config, None, gateway, self.metrics.clone());
272
+ worker.set_post_activate_hook(move |worker| {
273
+ if worker
274
+ .wft_manager
275
+ .most_recently_processed_event(&run_id)
276
+ .unwrap_or_default()
277
+ >= last_event
278
+ {
279
+ worker.initiate_shutdown();
280
+ }
281
+ });
282
+
283
+ self.workers.set_worker_for_task_queue(tq, worker)?;
284
+ Ok(())
285
+ }
286
+
287
+ /// Allow construction of workers with mocked poll responses during testing
288
+ #[cfg(test)]
289
+ pub(crate) fn reg_worker_sync(&self, worker: MockWorker) {
290
+ let sticky_q = self.get_sticky_q_name_for_worker(&worker.config);
291
+ let tq = worker.config.task_queue.clone();
292
+ let worker = Worker::new_with_pollers(
293
+ worker.config,
294
+ sticky_q,
295
+ self.server_gateway.clone(),
296
+ worker.wf_poller,
297
+ worker.act_poller,
298
+ self.metrics.clone(),
299
+ );
300
+ self.workers.set_worker_for_task_queue(tq, worker).unwrap();
301
+ }
302
+
303
+ #[cfg(test)]
304
+ pub(crate) fn outstanding_wfts(&self, tq: &str) -> usize {
305
+ self.worker(tq).unwrap().outstanding_workflow_tasks()
306
+ }
307
+ #[cfg(test)]
308
+ pub(crate) fn available_wft_permits(&self, tq: &str) -> usize {
309
+ self.worker(tq).unwrap().available_wft_permits()
310
+ }
311
+
312
+ fn get_sticky_q_name_for_worker(&self, config: &WorkerConfig) -> Option<String> {
313
+ if config.max_cached_workflows > 0 {
314
+ Some(format!(
315
+ "{}-{}-{}",
316
+ &self.init_options.gateway_opts.identity, &config.task_queue, *PROCCESS_UNIQ_ID
317
+ ))
318
+ } else {
319
+ None
320
+ }
321
+ }
322
+
323
+ fn worker(&self, tq: &str) -> Result<impl Deref<Target = Worker>, WorkerLookupErr> {
324
+ let worker = self.workers.get(tq);
325
+ if worker.is_err() && self.whole_core_shutdown.load(Ordering::Relaxed) {
326
+ return Err(WorkerLookupErr::Shutdown(tq.to_owned()));
327
+ }
328
+ worker
329
+ }
330
+ }
331
+
332
+ #[derive(Debug)]
333
+ enum WorkerLookupErr {
334
+ Shutdown(String),
335
+ NoWorker(String),
336
+ }
337
+
338
+ impl From<WorkerLookupErr> for PollWfError {
339
+ fn from(e: WorkerLookupErr) -> Self {
340
+ match e {
341
+ WorkerLookupErr::Shutdown(_) => Self::ShutDown,
342
+ WorkerLookupErr::NoWorker(s) => Self::NoWorkerForQueue(s),
343
+ }
344
+ }
345
+ }
346
+
347
+ impl From<WorkerLookupErr> for PollActivityError {
348
+ fn from(e: WorkerLookupErr) -> Self {
349
+ match e {
350
+ WorkerLookupErr::Shutdown(_) => Self::ShutDown,
351
+ WorkerLookupErr::NoWorker(s) => Self::NoWorkerForQueue(s),
352
+ }
353
+ }
354
+ }
355
+
356
+ impl From<WorkerLookupErr> for CompleteWfError {
357
+ fn from(e: WorkerLookupErr) -> Self {
358
+ match e {
359
+ WorkerLookupErr::Shutdown(s) | WorkerLookupErr::NoWorker(s) => {
360
+ Self::NoWorkerForQueue(s)
361
+ }
362
+ }
363
+ }
364
+ }
365
+
366
+ impl From<WorkerLookupErr> for CompleteActivityError {
367
+ fn from(e: WorkerLookupErr) -> Self {
368
+ match e {
369
+ WorkerLookupErr::Shutdown(s) | WorkerLookupErr::NoWorker(s) => {
370
+ Self::NoWorkerForQueue(s)
371
+ }
372
+ }
373
+ }
374
+ }
@@ -1,31 +1,7 @@
1
- use log::{Level, LevelFilter, Log, Metadata, Record};
1
+ use log::{LevelFilter, Log, Metadata, Record};
2
2
  use ringbuf::{Consumer, Producer, RingBuffer};
3
- use std::{
4
- sync::Mutex,
5
- time::{Duration, SystemTime, UNIX_EPOCH},
6
- };
7
-
8
- /// A log line (which ultimately came from a tracing event) exported from Core->Lang
9
- #[derive(Debug)]
10
- pub struct CoreLog {
11
- /// Log message
12
- pub message: String,
13
- /// Time log was generated (not when it was exported to lang)
14
- pub timestamp: SystemTime,
15
- /// Message level
16
- pub level: Level,
17
- // KV pairs aren't meaningfully exposed yet to the log interface by tracing
18
- }
19
-
20
- impl CoreLog {
21
- /// Return timestamp as ms since epoch
22
- pub fn millis_since_epoch(&self) -> u128 {
23
- self.timestamp
24
- .duration_since(UNIX_EPOCH)
25
- .unwrap_or(Duration::ZERO)
26
- .as_millis()
27
- }
28
- }
3
+ use std::{sync::Mutex, time::SystemTime};
4
+ use temporal_sdk_core_api::CoreLog;
29
5
 
30
6
  pub(crate) struct CoreExportLogger {
31
7
  logs_in: Mutex<Producer<CoreLog>>,
@@ -0,0 +1,162 @@
1
+ use parking_lot::RwLock;
2
+ use slotmap::SlotMap;
3
+ use std::collections::{HashMap, VecDeque};
4
+ use temporal_sdk_core_protos::coresdk::workflow_activation::{
5
+ remove_from_cache::EvictionReason, RemoveFromCache,
6
+ };
7
+
8
+ /// Tracks pending activations using an internal queue, while also allowing lookup and removal of
9
+ /// any pending activations by run ID.
10
+ #[derive(Default)]
11
+ pub struct PendingActivations {
12
+ inner: RwLock<PaInner>,
13
+ }
14
+
15
+ slotmap::new_key_type! { struct ActivationKey; }
16
+
17
+ #[derive(Default)]
18
+ struct PaInner {
19
+ activations: SlotMap<ActivationKey, PendingActInfo>,
20
+ by_run_id: HashMap<String, ActivationKey>,
21
+ // Holds the actual queue of activations
22
+ queue: VecDeque<ActivationKey>,
23
+ }
24
+
25
+ pub struct PendingActInfo {
26
+ pub needs_eviction: Option<RemoveFromCache>,
27
+ pub run_id: String,
28
+ }
29
+
30
+ impl PendingActivations {
31
+ /// Indicate that a run needs to be activated
32
+ pub fn notify_needs_activation(&self, run_id: &str) {
33
+ let mut inner = self.inner.write();
34
+
35
+ if inner.by_run_id.get(run_id).is_none() {
36
+ let key = inner.activations.insert(PendingActInfo {
37
+ needs_eviction: None,
38
+ run_id: run_id.to_string(),
39
+ });
40
+ inner.by_run_id.insert(run_id.to_string(), key);
41
+ inner.queue.push_back(key);
42
+ };
43
+ }
44
+ pub fn notify_needs_eviction(&self, run_id: &str, message: String, reason: EvictionReason) {
45
+ let mut inner = self.inner.write();
46
+
47
+ let evictjob = RemoveFromCache {
48
+ message,
49
+ reason: reason as i32,
50
+ };
51
+
52
+ if let Some(key) = inner.by_run_id.get(run_id).copied() {
53
+ let act = inner
54
+ .activations
55
+ .get_mut(key)
56
+ .expect("PA run id mapping is always in sync with slot map");
57
+ act.needs_eviction = Some(evictjob);
58
+ } else {
59
+ let key = inner.activations.insert(PendingActInfo {
60
+ needs_eviction: Some(evictjob),
61
+ run_id: run_id.to_string(),
62
+ });
63
+ inner.by_run_id.insert(run_id.to_string(), key);
64
+ inner.queue.push_back(key);
65
+ };
66
+ }
67
+
68
+ pub fn pop_first_matching(&self, predicate: impl Fn(&str) -> bool) -> Option<PendingActInfo> {
69
+ let mut inner = self.inner.write();
70
+ let mut key_queue = inner.queue.iter().copied();
71
+ let maybe_key = key_queue.position(|k| {
72
+ inner
73
+ .activations
74
+ .get(k)
75
+ .map_or(false, |activation| predicate(&activation.run_id))
76
+ });
77
+
78
+ let maybe_key = maybe_key.map(|pos| inner.queue.remove(pos).unwrap());
79
+ maybe_key.and_then(|key| {
80
+ if let Some(pa) = inner.activations.remove(key) {
81
+ inner.by_run_id.remove(&pa.run_id);
82
+ Some(pa)
83
+ } else {
84
+ // Keys no longer in the slot map are ignored, since they may have been removed
85
+ // by run id or anything else. Try to pop the next thing from the queue. Recurse
86
+ // to avoid double mutable borrow.
87
+ drop(inner); // Will deadlock when we recurse w/o this
88
+ self.pop_first_matching(predicate)
89
+ }
90
+ })
91
+ }
92
+
93
+ #[cfg(test)]
94
+ pub fn pop(&self) -> Option<PendingActInfo> {
95
+ self.pop_first_matching(|_| true)
96
+ }
97
+
98
+ pub fn has_pending(&self, run_id: &str) -> bool {
99
+ self.inner.read().by_run_id.contains_key(run_id)
100
+ }
101
+
102
+ pub fn remove_all_with_run_id(&self, run_id: &str) {
103
+ let mut inner = self.inner.write();
104
+
105
+ if let Some(k) = inner.by_run_id.remove(run_id) {
106
+ inner.activations.remove(k);
107
+ }
108
+ }
109
+ }
110
+
111
+ #[cfg(test)]
112
+ mod tests {
113
+ use super::*;
114
+
115
+ #[test]
116
+ fn merges_same_ids_with_evictions() {
117
+ let pas = PendingActivations::default();
118
+ let rid1 = "1";
119
+ let rid2 = "2";
120
+ pas.notify_needs_activation(rid1);
121
+ pas.notify_needs_eviction(rid1, "whatever".to_string(), EvictionReason::Unspecified);
122
+ pas.notify_needs_eviction(rid2, "whatever".to_string(), EvictionReason::Unspecified);
123
+ pas.notify_needs_activation(rid2);
124
+ assert!(pas.has_pending(rid1));
125
+ assert!(pas.has_pending(rid2));
126
+ let last = pas.pop().unwrap();
127
+ assert_eq!(&last.run_id, &rid1);
128
+ assert!(!pas.has_pending(rid1));
129
+ assert!(pas.has_pending(rid2));
130
+ // Should only be one id 2, they are all merged
131
+ let last = pas.pop().unwrap();
132
+ assert_eq!(&last.run_id, &rid2);
133
+ assert!(!pas.has_pending(rid2));
134
+ assert!(pas.pop().is_none());
135
+ }
136
+
137
+ #[test]
138
+ fn can_remove_all_with_id() {
139
+ let pas = PendingActivations::default();
140
+ let remove_me = "2";
141
+ pas.notify_needs_activation("1");
142
+ pas.notify_needs_activation(remove_me);
143
+ pas.notify_needs_activation("3");
144
+ pas.remove_all_with_run_id(remove_me);
145
+ assert!(!pas.has_pending(remove_me));
146
+ assert_eq!(&pas.pop().unwrap().run_id, "1");
147
+ assert_eq!(&pas.pop().unwrap().run_id, "3");
148
+ assert!(pas.pop().is_none());
149
+ }
150
+
151
+ #[test]
152
+ fn can_ignore_specific_runs() {
153
+ let pas = PendingActivations::default();
154
+ pas.notify_needs_activation("1");
155
+ pas.notify_needs_activation("2");
156
+ assert_eq!(
157
+ &pas.pop_first_matching(|rid| rid != "1").unwrap().run_id,
158
+ "2"
159
+ );
160
+ assert_eq!(&pas.pop().unwrap().run_id, "1");
161
+ }
162
+ }
@@ -1,19 +1,13 @@
1
- mod gateway;
2
1
  mod poll_buffer;
3
- mod retry;
4
2
 
5
- #[cfg(test)]
6
- pub use gateway::{MockManualGateway, MockServerGatewayApis};
7
-
8
- pub use gateway::{
9
- ClientTlsConfig, GatewayRef, RetryConfig, ServerGateway, ServerGatewayApis,
10
- ServerGatewayOptions, ServerGatewayOptionsBuilder, TlsConfig,
11
- };
12
3
  pub use poll_buffer::{
13
4
  new_activity_task_buffer, new_workflow_task_buffer, PollActivityTaskBuffer,
14
5
  PollWorkflowTaskBuffer, WorkflowTaskPoller,
15
6
  };
16
- pub use retry::RetryGateway;
7
+ pub use temporal_client::{
8
+ ClientTlsConfig, RetryConfig, RetryGateway, ServerGateway, ServerGatewayApis,
9
+ ServerGatewayOptions, ServerGatewayOptionsBuilder, TlsConfig,
10
+ };
17
11
 
18
12
  use temporal_sdk_core_protos::temporal::api::workflowservice::v1::{
19
13
  PollActivityTaskQueueResponse, PollWorkflowTaskQueueResponse,
@@ -21,21 +15,9 @@ use temporal_sdk_core_protos::temporal::api::workflowservice::v1::{
21
15
 
22
16
  #[cfg(test)]
23
17
  use futures::Future;
24
- use tonic::Code;
25
18
 
26
19
  pub type Result<T, E = tonic::Status> = std::result::Result<T, E>;
27
20
 
28
- /// List of gRPC error codes that client will retry.
29
- pub const RETRYABLE_ERROR_CODES: [Code; 7] = [
30
- Code::DataLoss,
31
- Code::Internal,
32
- Code::Unknown,
33
- Code::ResourceExhausted,
34
- Code::Aborted,
35
- Code::OutOfRange,
36
- Code::Unavailable,
37
- ];
38
-
39
21
  /// A trait for things that poll the server. Hides complexity of concurrent polling or polling
40
22
  /// on sticky/nonsticky queues simultaneously.
41
23
  #[cfg_attr(test, mockall::automock)]
@@ -236,9 +236,9 @@ pub fn new_activity_task_buffer(
236
236
  #[cfg(test)]
237
237
  mod tests {
238
238
  use super::*;
239
- use crate::pollers::MockManualGateway;
240
239
  use futures::FutureExt;
241
240
  use std::time::Duration;
241
+ use temporal_client::MockManualGateway;
242
242
  use tokio::{select, sync::mpsc::channel};
243
243
 
244
244
  #[tokio::test]