@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.
- package/Cargo.lock +339 -226
- package/Cargo.toml +7 -3
- package/common.js +50 -0
- package/index.d.ts +7 -0
- package/index.js +12 -0
- package/package.json +7 -4
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/index.node +0 -0
- package/{index.node → releases/index.node} +0 -0
- package/releases/x86_64-apple-darwin/index.node +0 -0
- package/releases/x86_64-pc-windows-msvc/index.node +0 -0
- package/releases/x86_64-unknown-linux-gnu/index.node +0 -0
- package/scripts/build.js +10 -50
- package/sdk-core/.buildkite/docker/Dockerfile +1 -1
- package/sdk-core/.buildkite/docker/docker-compose.yaml +2 -2
- package/sdk-core/.buildkite/pipeline.yml +2 -0
- package/sdk-core/Cargo.toml +1 -88
- package/sdk-core/README.md +30 -6
- package/sdk-core/bridge-ffi/Cargo.toml +24 -0
- package/sdk-core/bridge-ffi/LICENSE.txt +23 -0
- package/sdk-core/bridge-ffi/build.rs +25 -0
- package/sdk-core/bridge-ffi/include/sdk-core-bridge.h +216 -0
- package/sdk-core/bridge-ffi/src/lib.rs +829 -0
- package/sdk-core/bridge-ffi/src/wrappers.rs +193 -0
- package/sdk-core/client/Cargo.toml +32 -0
- package/sdk-core/{src/pollers/gateway.rs → client/src/lib.rs} +101 -195
- package/sdk-core/client/src/metrics.rs +89 -0
- package/sdk-core/client/src/mocks.rs +167 -0
- package/sdk-core/{src/pollers → client/src}/retry.rs +172 -14
- package/sdk-core/core/Cargo.toml +96 -0
- package/sdk-core/{src → core/src}/core_tests/activity_tasks.rs +193 -37
- package/sdk-core/{src → core/src}/core_tests/child_workflows.rs +14 -14
- package/sdk-core/{src → core/src}/core_tests/determinism.rs +8 -8
- package/sdk-core/core/src/core_tests/local_activities.rs +328 -0
- package/sdk-core/{src → core/src}/core_tests/mod.rs +6 -9
- package/sdk-core/{src → core/src}/core_tests/queries.rs +45 -52
- package/sdk-core/{src → core/src}/core_tests/replay_flag.rs +8 -12
- package/sdk-core/{src → core/src}/core_tests/workers.rs +120 -33
- package/sdk-core/{src → core/src}/core_tests/workflow_cancels.rs +16 -26
- package/sdk-core/{src → core/src}/core_tests/workflow_tasks.rs +264 -286
- package/sdk-core/core/src/lib.rs +374 -0
- package/sdk-core/{src → core/src}/log_export.rs +3 -27
- package/sdk-core/core/src/pending_activations.rs +162 -0
- package/sdk-core/{src → core/src}/pollers/mod.rs +4 -22
- package/sdk-core/{src → core/src}/pollers/poll_buffer.rs +1 -1
- package/sdk-core/core/src/protosext/mod.rs +396 -0
- package/sdk-core/core/src/replay/mod.rs +210 -0
- package/sdk-core/core/src/retry_logic.rs +144 -0
- package/sdk-core/{src → core/src}/telemetry/metrics.rs +3 -58
- package/sdk-core/{src → core/src}/telemetry/mod.rs +8 -8
- package/sdk-core/{src → core/src}/telemetry/prometheus_server.rs +0 -0
- package/sdk-core/{src → core/src}/test_help/mod.rs +34 -73
- package/sdk-core/{src → core/src}/worker/activities/activity_heartbeat_manager.rs +95 -42
- package/sdk-core/core/src/worker/activities/local_activities.rs +973 -0
- package/sdk-core/{src → core/src}/worker/activities.rs +52 -33
- package/sdk-core/{src → core/src}/worker/dispatcher.rs +8 -6
- package/sdk-core/{src → core/src}/worker/mod.rs +305 -195
- package/sdk-core/core/src/worker/wft_delivery.rs +81 -0
- package/sdk-core/{src → core/src}/workflow/bridge.rs +5 -2
- package/sdk-core/{src → core/src}/workflow/driven_workflow.rs +17 -7
- package/sdk-core/{src → core/src}/workflow/history_update.rs +33 -7
- package/sdk-core/{src → core/src/workflow}/machines/activity_state_machine.rs +26 -26
- package/sdk-core/{src → core/src/workflow}/machines/cancel_external_state_machine.rs +8 -11
- package/sdk-core/{src → core/src/workflow}/machines/cancel_workflow_state_machine.rs +19 -21
- package/sdk-core/{src → core/src/workflow}/machines/child_workflow_state_machine.rs +19 -21
- package/sdk-core/{src → core/src/workflow}/machines/complete_workflow_state_machine.rs +3 -5
- package/sdk-core/{src → core/src/workflow}/machines/continue_as_new_workflow_state_machine.rs +18 -18
- package/sdk-core/{src → core/src/workflow}/machines/fail_workflow_state_machine.rs +5 -6
- package/sdk-core/core/src/workflow/machines/local_activity_state_machine.rs +1451 -0
- package/sdk-core/{src → core/src/workflow}/machines/mod.rs +54 -107
- package/sdk-core/{src → core/src/workflow}/machines/mutable_side_effect_state_machine.rs +0 -0
- package/sdk-core/{src → core/src/workflow}/machines/patch_state_machine.rs +29 -30
- package/sdk-core/{src → core/src/workflow}/machines/side_effect_state_machine.rs +0 -0
- package/sdk-core/{src → core/src/workflow}/machines/signal_external_state_machine.rs +17 -19
- package/sdk-core/{src → core/src/workflow}/machines/timer_state_machine.rs +20 -21
- package/sdk-core/{src → core/src/workflow}/machines/transition_coverage.rs +5 -2
- package/sdk-core/{src → core/src/workflow}/machines/upsert_search_attributes_state_machine.rs +0 -0
- package/sdk-core/core/src/workflow/machines/workflow_machines/local_acts.rs +96 -0
- package/sdk-core/{src → core/src/workflow}/machines/workflow_machines.rs +344 -160
- package/sdk-core/{src → core/src/workflow}/machines/workflow_task_state_machine.rs +1 -1
- package/sdk-core/{src → core/src}/workflow/mod.rs +200 -39
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/cache_manager.rs +0 -0
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/concurrency_manager.rs +38 -5
- package/sdk-core/{src → core/src}/workflow/workflow_tasks/mod.rs +297 -81
- package/sdk-core/{test_utils → core-api}/Cargo.toml +10 -7
- package/sdk-core/{src → core-api/src}/errors.rs +42 -90
- package/sdk-core/core-api/src/lib.rs +158 -0
- package/sdk-core/{src/worker/config.rs → core-api/src/worker.rs} +18 -23
- package/sdk-core/etc/deps.svg +156 -0
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +5 -5
- package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/no_handle_conversions_require_into_fail.stderr +3 -5
- package/sdk-core/fsm/rustfsm_trait/src/lib.rs +7 -1
- package/sdk-core/histories/fail_wf_task.bin +0 -0
- package/sdk-core/histories/timer_workflow_history.bin +0 -0
- package/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +44 -13
- package/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +19 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/common.proto +1 -1
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +9 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/namespace.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/reset.proto +1 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +13 -0
- package/sdk-core/protos/api_upstream/temporal/api/enums/v1/workflow.proto +14 -7
- package/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +176 -18
- package/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -0
- package/sdk-core/protos/api_upstream/temporal/api/query/v1/message.proto +11 -0
- package/sdk-core/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +3 -0
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +156 -7
- package/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +135 -104
- package/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +78 -0
- package/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +78 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/bridge.proto +205 -0
- package/sdk-core/protos/local/temporal/sdk/core/bridge/service.proto +61 -0
- package/sdk-core/protos/local/{child_workflow.proto → temporal/sdk/core/child_workflow/child_workflow.proto} +1 -1
- package/sdk-core/protos/local/{common.proto → temporal/sdk/core/common/common.proto} +5 -3
- package/sdk-core/protos/local/{core_interface.proto → temporal/sdk/core/core_interface.proto} +10 -10
- package/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +30 -0
- package/sdk-core/protos/local/{workflow_activation.proto → temporal/sdk/core/workflow_activation/workflow_activation.proto} +35 -11
- package/sdk-core/protos/local/{workflow_commands.proto → temporal/sdk/core/workflow_commands/workflow_commands.proto} +55 -4
- package/sdk-core/protos/local/{workflow_completion.proto → temporal/sdk/core/workflow_completion/workflow_completion.proto} +3 -3
- package/sdk-core/sdk/Cargo.toml +32 -0
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/conversions.rs +0 -0
- package/sdk-core/sdk/src/lib.rs +699 -0
- package/sdk-core/sdk/src/payload_converter.rs +11 -0
- package/sdk-core/sdk/src/workflow_context/options.rs +180 -0
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_context.rs +201 -124
- package/sdk-core/{src/prototype_rust_sdk → sdk/src}/workflow_future.rs +63 -30
- package/sdk-core/sdk-core-protos/Cargo.toml +10 -0
- package/sdk-core/sdk-core-protos/build.rs +28 -6
- package/sdk-core/sdk-core-protos/src/constants.rs +7 -0
- package/sdk-core/{src/test_help → sdk-core-protos/src}/history_builder.rs +134 -49
- package/sdk-core/sdk-core-protos/src/history_info.rs +216 -0
- package/sdk-core/sdk-core-protos/src/lib.rs +594 -168
- package/sdk-core/sdk-core-protos/src/task_token.rs +38 -0
- package/sdk-core/sdk-core-protos/src/utilities.rs +14 -0
- package/sdk-core/test-utils/Cargo.toml +32 -0
- package/sdk-core/{src/test_help → test-utils/src}/canned_histories.rs +59 -78
- package/sdk-core/test-utils/src/histfetch.rs +28 -0
- package/sdk-core/{test_utils → test-utils}/src/lib.rs +131 -68
- package/sdk-core/tests/integ_tests/client_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +11 -7
- package/sdk-core/tests/integ_tests/polling_tests.rs +12 -11
- package/sdk-core/tests/integ_tests/queries_tests.rs +82 -78
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +91 -71
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +3 -4
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +2 -4
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +4 -6
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +4 -6
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -4
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +496 -0
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +5 -8
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +125 -0
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +7 -13
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +33 -5
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +12 -16
- package/sdk-core/tests/integ_tests/workflow_tests.rs +85 -82
- package/sdk-core/tests/load_tests.rs +6 -6
- package/sdk-core/tests/main.rs +2 -2
- package/src/conversions.rs +24 -21
- package/src/errors.rs +8 -0
- package/src/lib.rs +323 -211
- package/sdk-core/protos/local/activity_result.proto +0 -46
- package/sdk-core/protos/local/activity_task.proto +0 -66
- package/sdk-core/src/core_tests/retry.rs +0 -147
- package/sdk-core/src/lib.rs +0 -403
- package/sdk-core/src/machines/local_activity_state_machine.rs +0 -117
- package/sdk-core/src/pending_activations.rs +0 -249
- package/sdk-core/src/protosext/mod.rs +0 -160
- package/sdk-core/src/prototype_rust_sdk.rs +0 -412
- package/sdk-core/src/task_token.rs +0 -20
- 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::{
|
|
1
|
+
use log::{LevelFilter, Log, Metadata, Record};
|
|
2
2
|
use ringbuf::{Consumer, Producer, RingBuffer};
|
|
3
|
-
use std::{
|
|
4
|
-
|
|
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
|
|
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]
|