@temporalio/core-bridge 1.13.0 → 1.13.2
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 +239 -382
- package/Cargo.toml +11 -11
- package/lib/native.d.ts +10 -3
- package/package.json +3 -3
- package/releases/aarch64-apple-darwin/index.node +0 -0
- package/releases/aarch64-unknown-linux-gnu/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/sdk-core/.cargo/config.toml +71 -11
- package/sdk-core/.clippy.toml +1 -0
- package/sdk-core/.github/workflows/heavy.yml +2 -0
- package/sdk-core/.github/workflows/per-pr.yml +50 -18
- package/sdk-core/ARCHITECTURE.md +44 -48
- package/sdk-core/Cargo.toml +26 -7
- package/sdk-core/README.md +4 -0
- package/sdk-core/arch_docs/diagrams/TimerMachine_Coverage.puml +14 -0
- package/sdk-core/arch_docs/diagrams/initial_event_history.png +0 -0
- package/sdk-core/arch_docs/sdks_intro.md +299 -0
- package/sdk-core/client/Cargo.toml +8 -7
- package/sdk-core/client/src/callback_based.rs +1 -2
- package/sdk-core/client/src/lib.rs +485 -299
- package/sdk-core/client/src/metrics.rs +32 -8
- package/sdk-core/client/src/proxy.rs +124 -5
- package/sdk-core/client/src/raw.rs +598 -307
- package/sdk-core/client/src/replaceable.rs +253 -0
- package/sdk-core/client/src/retry.rs +9 -6
- package/sdk-core/client/src/worker_registry/mod.rs +19 -3
- package/sdk-core/client/src/workflow_handle/mod.rs +20 -17
- package/sdk-core/core/Cargo.toml +100 -31
- package/sdk-core/core/src/core_tests/activity_tasks.rs +55 -225
- package/sdk-core/core/src/core_tests/mod.rs +2 -8
- package/sdk-core/core/src/core_tests/queries.rs +3 -5
- package/sdk-core/core/src/core_tests/replay_flag.rs +3 -62
- package/sdk-core/core/src/core_tests/updates.rs +4 -5
- package/sdk-core/core/src/core_tests/workers.rs +4 -3
- package/sdk-core/core/src/core_tests/workflow_cancels.rs +10 -7
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +28 -291
- package/sdk-core/core/src/ephemeral_server/mod.rs +15 -3
- package/sdk-core/core/src/internal_flags.rs +11 -1
- package/sdk-core/core/src/lib.rs +50 -36
- package/sdk-core/core/src/pollers/mod.rs +5 -5
- package/sdk-core/core/src/pollers/poll_buffer.rs +2 -2
- package/sdk-core/core/src/protosext/mod.rs +13 -5
- package/sdk-core/core/src/protosext/protocol_messages.rs +4 -11
- package/sdk-core/core/src/retry_logic.rs +256 -108
- package/sdk-core/core/src/telemetry/metrics.rs +1 -0
- package/sdk-core/core/src/telemetry/mod.rs +8 -2
- package/sdk-core/core/src/telemetry/prometheus_meter.rs +2 -2
- package/sdk-core/core/src/test_help/integ_helpers.rs +971 -0
- package/sdk-core/core/src/test_help/mod.rs +10 -1100
- package/sdk-core/core/src/test_help/unit_helpers.rs +218 -0
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +42 -6
- package/sdk-core/core/src/worker/activities/local_activities.rs +19 -19
- package/sdk-core/core/src/worker/activities.rs +10 -3
- package/sdk-core/core/src/worker/client/mocks.rs +3 -3
- package/sdk-core/core/src/worker/client.rs +130 -93
- package/sdk-core/core/src/worker/heartbeat.rs +12 -13
- package/sdk-core/core/src/worker/mod.rs +31 -21
- package/sdk-core/core/src/worker/nexus.rs +14 -3
- package/sdk-core/core/src/worker/slot_provider.rs +9 -0
- package/sdk-core/core/src/worker/tuner.rs +159 -0
- package/sdk-core/core/src/worker/workflow/history_update.rs +3 -265
- package/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +1 -54
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +0 -82
- package/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +0 -67
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -192
- package/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +0 -43
- package/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +6 -554
- package/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +0 -71
- package/sdk-core/core/src/worker/workflow/machines/nexus_operation_state_machine.rs +102 -3
- package/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +10 -539
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +0 -139
- package/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +1 -119
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +6 -63
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +9 -4
- package/sdk-core/core/src/worker/workflow/mod.rs +5 -1
- package/sdk-core/core/src/worker/workflow/workflow_stream.rs +8 -3
- package/sdk-core/core-api/Cargo.toml +4 -4
- package/sdk-core/core-api/src/envconfig.rs +153 -54
- package/sdk-core/core-api/src/lib.rs +68 -0
- package/sdk-core/core-api/src/telemetry/metrics.rs +2 -1
- package/sdk-core/core-api/src/telemetry.rs +13 -0
- package/sdk-core/core-c-bridge/Cargo.toml +13 -8
- package/sdk-core/core-c-bridge/include/temporal-sdk-core-c-bridge.h +184 -22
- package/sdk-core/core-c-bridge/src/client.rs +462 -184
- package/sdk-core/core-c-bridge/src/envconfig.rs +314 -0
- package/sdk-core/core-c-bridge/src/lib.rs +1 -0
- package/sdk-core/core-c-bridge/src/random.rs +4 -4
- package/sdk-core/core-c-bridge/src/runtime.rs +22 -23
- package/sdk-core/core-c-bridge/src/testing.rs +1 -4
- package/sdk-core/core-c-bridge/src/tests/context.rs +31 -31
- package/sdk-core/core-c-bridge/src/tests/mod.rs +32 -28
- package/sdk-core/core-c-bridge/src/tests/utils.rs +7 -7
- package/sdk-core/core-c-bridge/src/worker.rs +319 -66
- package/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +6 -1
- package/sdk-core/fsm/rustfsm_procmacro/tests/trybuild/dupe_transitions_fail.stderr +5 -5
- package/sdk-core/sdk/Cargo.toml +8 -2
- package/sdk-core/sdk/src/activity_context.rs +1 -1
- package/sdk-core/sdk/src/app_data.rs +1 -1
- package/sdk-core/sdk/src/interceptors.rs +1 -4
- package/sdk-core/sdk/src/lib.rs +1 -5
- package/sdk-core/sdk/src/workflow_context/options.rs +10 -1
- package/sdk-core/sdk/src/workflow_future.rs +1 -1
- package/sdk-core/sdk-core-protos/Cargo.toml +6 -6
- package/sdk-core/sdk-core-protos/build.rs +10 -23
- package/sdk-core/sdk-core-protos/protos/api_upstream/.github/workflows/create-release.yml +9 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +254 -5
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +234 -5
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/deployment/v1/message.proto +6 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/namespace/v1/message.proto +6 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +60 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +30 -6
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +2 -0
- package/sdk-core/{test-utils → sdk-core-protos}/src/canned_histories.rs +5 -5
- package/sdk-core/sdk-core-protos/src/history_builder.rs +2 -2
- package/sdk-core/sdk-core-protos/src/lib.rs +25 -9
- package/sdk-core/sdk-core-protos/src/test_utils.rs +89 -0
- package/sdk-core/sdk-core-protos/src/utilities.rs +14 -5
- package/sdk-core/tests/c_bridge_smoke_test.c +10 -0
- package/sdk-core/tests/cloud_tests.rs +10 -8
- package/sdk-core/tests/common/http_proxy.rs +134 -0
- package/sdk-core/{test-utils/src/lib.rs → tests/common/mod.rs} +214 -281
- package/sdk-core/{test-utils/src → tests/common}/workflows.rs +4 -3
- package/sdk-core/tests/fuzzy_workflow.rs +1 -1
- package/sdk-core/tests/global_metric_tests.rs +8 -7
- package/sdk-core/tests/heavy_tests.rs +7 -3
- package/sdk-core/tests/integ_tests/client_tests.rs +111 -24
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +14 -9
- package/sdk-core/tests/integ_tests/heartbeat_tests.rs +4 -4
- package/sdk-core/tests/integ_tests/metrics_tests.rs +114 -14
- package/sdk-core/tests/integ_tests/pagination_tests.rs +273 -0
- package/sdk-core/tests/integ_tests/polling_tests.rs +311 -93
- package/sdk-core/tests/integ_tests/queries_tests.rs +4 -4
- package/sdk-core/tests/integ_tests/update_tests.rs +13 -7
- package/sdk-core/tests/integ_tests/visibility_tests.rs +26 -9
- package/sdk-core/tests/integ_tests/worker_tests.rs +668 -13
- package/sdk-core/tests/integ_tests/worker_versioning_tests.rs +40 -24
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +244 -11
- package/sdk-core/tests/integ_tests/workflow_tests/appdata_propagation.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +78 -2
- package/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +61 -2
- package/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +465 -7
- package/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +41 -2
- package/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +315 -3
- package/sdk-core/tests/integ_tests/workflow_tests/eager.rs +1 -1
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +1990 -14
- package/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +65 -2
- package/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +123 -23
- package/sdk-core/tests/integ_tests/workflow_tests/patches.rs +525 -3
- package/sdk-core/tests/integ_tests/workflow_tests/replay.rs +65 -16
- package/sdk-core/tests/integ_tests/workflow_tests/resets.rs +32 -23
- package/sdk-core/tests/integ_tests/workflow_tests/signals.rs +126 -5
- package/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +1 -2
- package/sdk-core/tests/integ_tests/workflow_tests/timers.rs +124 -8
- package/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +62 -2
- package/sdk-core/tests/integ_tests/workflow_tests.rs +67 -8
- package/sdk-core/tests/main.rs +26 -17
- package/sdk-core/tests/manual_tests.rs +5 -1
- package/sdk-core/tests/runner.rs +22 -40
- package/sdk-core/tests/shared_tests/mod.rs +1 -1
- package/sdk-core/tests/shared_tests/priority.rs +1 -1
- package/sdk-core/{core/benches/workflow_replay.rs → tests/workflow_replay_bench.rs} +10 -5
- package/src/client.rs +97 -20
- package/src/helpers/callbacks.rs +4 -4
- package/src/helpers/errors.rs +7 -1
- package/src/helpers/handles.rs +1 -0
- package/src/helpers/try_from_js.rs +4 -3
- package/src/lib.rs +3 -2
- package/src/metrics.rs +3 -0
- package/src/runtime.rs +5 -2
- package/src/worker.rs +9 -12
- package/ts/native.ts +13 -3
- package/sdk-core/arch_docs/diagrams/workflow_internals.svg +0 -1
- package/sdk-core/core/src/core_tests/child_workflows.rs +0 -281
- package/sdk-core/core/src/core_tests/determinism.rs +0 -318
- package/sdk-core/core/src/core_tests/local_activities.rs +0 -1442
- package/sdk-core/test-utils/Cargo.toml +0 -38
- package/sdk-core/test-utils/src/histfetch.rs +0 -28
- package/sdk-core/test-utils/src/interceptors.rs +0 -46
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
use crate::NamespacedClient;
|
|
2
|
+
use std::{
|
|
3
|
+
borrow::Cow,
|
|
4
|
+
sync::{
|
|
5
|
+
Arc, RwLock,
|
|
6
|
+
atomic::{AtomicU32, Ordering},
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/// A client wrapper that allows replacing the underlying client at a later point in time.
|
|
11
|
+
/// Clones of this struct have a shared reference to the underlying client, and each clone also
|
|
12
|
+
/// has its own cached clone of the underlying client. Before every service call, a check is made
|
|
13
|
+
/// whether the shared client was replaced, and the cached clone is updated accordingly.
|
|
14
|
+
///
|
|
15
|
+
/// This struct is fully thread-safe, and it works in a lock-free manner except when the client is
|
|
16
|
+
/// being replaced. A read-write lock is used then, with minimal locking time.
|
|
17
|
+
#[derive(Debug)]
|
|
18
|
+
pub struct SharedReplaceableClient<C>
|
|
19
|
+
where
|
|
20
|
+
C: Clone + Send + Sync,
|
|
21
|
+
{
|
|
22
|
+
shared_data: Arc<SharedClientData<C>>,
|
|
23
|
+
cloned_client: C,
|
|
24
|
+
cloned_generation: u32,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#[derive(Debug)]
|
|
28
|
+
struct SharedClientData<C>
|
|
29
|
+
where
|
|
30
|
+
C: Clone + Send + Sync,
|
|
31
|
+
{
|
|
32
|
+
client: RwLock<C>,
|
|
33
|
+
generation: AtomicU32,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
impl<C> SharedClientData<C>
|
|
37
|
+
where
|
|
38
|
+
C: Clone + Send + Sync,
|
|
39
|
+
{
|
|
40
|
+
fn fetch(&self) -> (C, u32) {
|
|
41
|
+
let lock = self.client.read().unwrap();
|
|
42
|
+
let client = lock.clone();
|
|
43
|
+
// Loading generation under lock to ensure the client won't be updated in the meantime.
|
|
44
|
+
let generation = self.generation.load(Ordering::Acquire);
|
|
45
|
+
(client, generation)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fn fetch_newer_than(&self, current_generation: u32) -> Option<(C, u32)> {
|
|
49
|
+
// fetch() will do a second atomic load, but it's necessary to avoid a race condition.
|
|
50
|
+
(current_generation != self.generation.load(Ordering::Acquire)).then(|| self.fetch())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn replace_client(&self, client: C) {
|
|
54
|
+
let mut lock = self.client.write().unwrap();
|
|
55
|
+
*lock = client;
|
|
56
|
+
// Updating generation under lock to guarantee consistency when multiple threads replace the
|
|
57
|
+
// client at the same time. The client stored last is always the one with latest generation.
|
|
58
|
+
self.generation.fetch_add(1, Ordering::AcqRel);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
impl<C> SharedReplaceableClient<C>
|
|
63
|
+
where
|
|
64
|
+
C: Clone + Send + Sync,
|
|
65
|
+
{
|
|
66
|
+
/// Creates the initial instance of replaceable client with the provided underlying client.
|
|
67
|
+
/// Use [`clone()`](Self::clone) method to create more instances that share the same underlying client.
|
|
68
|
+
pub fn new(client: C) -> Self {
|
|
69
|
+
let cloned_client = client.clone();
|
|
70
|
+
Self {
|
|
71
|
+
shared_data: Arc::new(SharedClientData {
|
|
72
|
+
client: RwLock::new(client),
|
|
73
|
+
generation: AtomicU32::new(0),
|
|
74
|
+
}),
|
|
75
|
+
cloned_client,
|
|
76
|
+
cloned_generation: 0,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/// Replaces the client for all instances that share this instance's underlying client.
|
|
81
|
+
pub fn replace_client(&self, new_client: C) {
|
|
82
|
+
self.shared_data.replace_client(new_client); // cloned_client will be updated on next mutable call
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/// Returns a clone of the underlying client.
|
|
86
|
+
pub fn inner_clone(&self) -> C {
|
|
87
|
+
self.inner_cow().into_owned()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// Returns an immutable reference to this instance's cached clone of the underlying client if
|
|
91
|
+
/// it's up to date, or a fresh clone of the shared client otherwise. Because it's an immutable
|
|
92
|
+
/// method, it will not update this instance's cached clone. For this reason, prefer to use
|
|
93
|
+
/// [`inner_mut_refreshed()`](Self::inner_mut_refreshed) when possible.
|
|
94
|
+
pub fn inner_cow(&self) -> Cow<'_, C> {
|
|
95
|
+
self.shared_data
|
|
96
|
+
.fetch_newer_than(self.cloned_generation)
|
|
97
|
+
.map(|(c, _)| Cow::Owned(c))
|
|
98
|
+
.unwrap_or_else(|| Cow::Borrowed(&self.cloned_client))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Returns a mutable reference to this instance's cached clone of the underlying client. If the
|
|
102
|
+
/// cached clone is not up to date, it's refreshed before the reference is returned. This method
|
|
103
|
+
/// is called automatically by most other mutable methods, in particular by all service calls,
|
|
104
|
+
/// so most of the time it doesn't need to be called directly.
|
|
105
|
+
///
|
|
106
|
+
/// While this method allows mutable access to the underlying client, any configuration changes
|
|
107
|
+
/// will not be shared with other instances, and will be lost if the client gets replaced from
|
|
108
|
+
/// anywhere. To make configuration changes, use [`replace_client()`](Self::replace_client) instead.
|
|
109
|
+
pub fn inner_mut_refreshed(&mut self) -> &mut C {
|
|
110
|
+
if let Some((client, generation)) =
|
|
111
|
+
self.shared_data.fetch_newer_than(self.cloned_generation)
|
|
112
|
+
{
|
|
113
|
+
self.cloned_client = client;
|
|
114
|
+
self.cloned_generation = generation;
|
|
115
|
+
}
|
|
116
|
+
&mut self.cloned_client
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
impl<C> Clone for SharedReplaceableClient<C>
|
|
121
|
+
where
|
|
122
|
+
C: Clone + Send + Sync,
|
|
123
|
+
{
|
|
124
|
+
/// Creates a new instance of replaceable client that shares the underlying client with this
|
|
125
|
+
/// instance. Replacing a client in either instance will replace it for both instances, and all
|
|
126
|
+
/// other clones too.
|
|
127
|
+
fn clone(&self) -> Self {
|
|
128
|
+
// self's cloned_client could've been modified through a mutable reference,
|
|
129
|
+
// so for consistent behavior, we need to fetch it from shared_data.
|
|
130
|
+
let (client, generation) = self.shared_data.fetch();
|
|
131
|
+
Self {
|
|
132
|
+
shared_data: self.shared_data.clone(),
|
|
133
|
+
cloned_client: client,
|
|
134
|
+
cloned_generation: generation,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
impl<C> NamespacedClient for SharedReplaceableClient<C>
|
|
140
|
+
where
|
|
141
|
+
C: NamespacedClient + Clone + Send + Sync,
|
|
142
|
+
{
|
|
143
|
+
fn namespace(&self) -> String {
|
|
144
|
+
self.inner_cow().namespace()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fn identity(&self) -> String {
|
|
148
|
+
self.inner_cow().identity()
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#[cfg(test)]
|
|
153
|
+
mod tests {
|
|
154
|
+
use super::*;
|
|
155
|
+
use crate::NamespacedClient;
|
|
156
|
+
use std::borrow::Cow;
|
|
157
|
+
|
|
158
|
+
#[derive(Debug, Clone)]
|
|
159
|
+
struct StubClient {
|
|
160
|
+
identity: String,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
impl StubClient {
|
|
164
|
+
fn new(identity: &str) -> Self {
|
|
165
|
+
Self {
|
|
166
|
+
identity: identity.to_owned(),
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
impl NamespacedClient for StubClient {
|
|
172
|
+
fn namespace(&self) -> String {
|
|
173
|
+
"default".into()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fn identity(&self) -> String {
|
|
177
|
+
self.identity.clone()
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#[test]
|
|
182
|
+
fn cow_returns_reference_before_and_clone_after_refresh() {
|
|
183
|
+
let mut client = SharedReplaceableClient::new(StubClient::new("1"));
|
|
184
|
+
let Cow::Borrowed(inner) = client.inner_cow() else {
|
|
185
|
+
panic!("expected borrowed inner");
|
|
186
|
+
};
|
|
187
|
+
assert_eq!(inner.identity, "1");
|
|
188
|
+
|
|
189
|
+
client.replace_client(StubClient::new("2"));
|
|
190
|
+
let Cow::Owned(inner) = client.inner_cow() else {
|
|
191
|
+
panic!("expected owned inner");
|
|
192
|
+
};
|
|
193
|
+
assert_eq!(inner.identity, "2");
|
|
194
|
+
|
|
195
|
+
assert_eq!(client.inner_mut_refreshed().identity, "2");
|
|
196
|
+
let Cow::Borrowed(inner) = client.inner_cow() else {
|
|
197
|
+
panic!("expected borrowed inner");
|
|
198
|
+
};
|
|
199
|
+
assert_eq!(inner.identity, "2");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#[test]
|
|
203
|
+
fn client_replaced_in_clones() {
|
|
204
|
+
let original1 = SharedReplaceableClient::new(StubClient::new("1"));
|
|
205
|
+
let clone1 = original1.clone();
|
|
206
|
+
assert_eq!(original1.identity(), "1");
|
|
207
|
+
assert_eq!(clone1.identity(), "1");
|
|
208
|
+
|
|
209
|
+
original1.replace_client(StubClient::new("2"));
|
|
210
|
+
assert_eq!(original1.identity(), "2");
|
|
211
|
+
assert_eq!(clone1.identity(), "2");
|
|
212
|
+
|
|
213
|
+
let original2 = SharedReplaceableClient::new(StubClient::new("3"));
|
|
214
|
+
let clone2 = original2.clone();
|
|
215
|
+
assert_eq!(original2.identity(), "3");
|
|
216
|
+
assert_eq!(clone2.identity(), "3");
|
|
217
|
+
|
|
218
|
+
clone2.replace_client(StubClient::new("4"));
|
|
219
|
+
assert_eq!(original2.identity(), "4");
|
|
220
|
+
assert_eq!(clone2.identity(), "4");
|
|
221
|
+
assert_eq!(original1.identity(), "2");
|
|
222
|
+
assert_eq!(clone1.identity(), "2");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
#[test]
|
|
226
|
+
fn client_replaced_from_multiple_threads() {
|
|
227
|
+
let mut client = SharedReplaceableClient::new(StubClient::new("original"));
|
|
228
|
+
std::thread::scope(|scope| {
|
|
229
|
+
for thread_no in 0..100 {
|
|
230
|
+
let mut client = client.clone();
|
|
231
|
+
scope.spawn(move || {
|
|
232
|
+
for i in 0..1000 {
|
|
233
|
+
let old_generation = client.cloned_generation;
|
|
234
|
+
client.inner_mut_refreshed();
|
|
235
|
+
let current_generation = client.cloned_generation;
|
|
236
|
+
assert!(current_generation >= old_generation);
|
|
237
|
+
let replace_identity = format!("{thread_no}-{i}");
|
|
238
|
+
client.replace_client(StubClient::new(&replace_identity));
|
|
239
|
+
client.inner_mut_refreshed();
|
|
240
|
+
assert!(client.cloned_generation > current_generation);
|
|
241
|
+
let refreshed_identity = client.identity();
|
|
242
|
+
if refreshed_identity.split('-').next().unwrap() == thread_no.to_string() {
|
|
243
|
+
assert_eq!(replace_identity, refreshed_identity);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
client.inner_mut_refreshed();
|
|
250
|
+
assert_eq!(client.cloned_generation, 100_000);
|
|
251
|
+
assert!(client.identity().ends_with("-999"));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
use crate::{
|
|
2
|
-
|
|
2
|
+
ERROR_RETURNED_DUE_TO_SHORT_CIRCUIT, IsWorkerTaskLongPoll, MESSAGE_TOO_LARGE_KEY,
|
|
3
3
|
NamespacedClient, NoRetryOnMatching, Result, RetryConfig, raw::IsUserLongPoll,
|
|
4
4
|
};
|
|
5
5
|
use backoff::{Clock, SystemClock, backoff::Backoff, exponential::ExponentialBackoff};
|
|
@@ -98,13 +98,16 @@ impl<SG> RetryClient<SG> {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
impl NamespacedClient for RetryClient<
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
impl<SG> NamespacedClient for RetryClient<SG>
|
|
102
|
+
where
|
|
103
|
+
SG: NamespacedClient,
|
|
104
|
+
{
|
|
105
|
+
fn namespace(&self) -> String {
|
|
106
|
+
self.client.namespace()
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
fn
|
|
107
|
-
|
|
109
|
+
fn identity(&self) -> String {
|
|
110
|
+
self.client.identity()
|
|
108
111
|
}
|
|
109
112
|
}
|
|
110
113
|
|
|
@@ -22,6 +22,8 @@ pub trait SlotProvider: std::fmt::Debug {
|
|
|
22
22
|
fn task_queue(&self) -> &str;
|
|
23
23
|
/// Try to reserve a slot on this worker.
|
|
24
24
|
fn try_reserve_wft_slot(&self) -> Option<Box<dyn Slot + Send>>;
|
|
25
|
+
/// Get the worker deployment options for this worker, if using deployment-based versioning.
|
|
26
|
+
fn deployment_options(&self) -> Option<temporal_sdk_core_api::worker::WorkerDeploymentOptions>;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/// This trait represents a slot reserved for processing a WFT by a worker.
|
|
@@ -34,6 +36,14 @@ pub trait Slot {
|
|
|
34
36
|
) -> Result<(), anyhow::Error>;
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
/// Result of reserving a workflow task slot, including deployment options if applicable.
|
|
40
|
+
pub(crate) struct SlotReservation {
|
|
41
|
+
/// The reserved slot for processing the workflow task
|
|
42
|
+
pub slot: Box<dyn Slot + Send>,
|
|
43
|
+
/// Worker deployment options, if the worker is using deployment-based versioning
|
|
44
|
+
pub deployment_options: Option<temporal_sdk_core_api::worker::WorkerDeploymentOptions>,
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
|
|
38
48
|
struct SlotKey {
|
|
39
49
|
namespace: String,
|
|
@@ -71,12 +81,16 @@ impl SlotManagerImpl {
|
|
|
71
81
|
&self,
|
|
72
82
|
namespace: String,
|
|
73
83
|
task_queue: String,
|
|
74
|
-
) -> Option<
|
|
84
|
+
) -> Option<SlotReservation> {
|
|
75
85
|
let key = SlotKey::new(namespace, task_queue);
|
|
76
86
|
if let Some(p) = self.providers.get(&key)
|
|
77
87
|
&& let Some(slot) = p.try_reserve_wft_slot()
|
|
78
88
|
{
|
|
79
|
-
|
|
89
|
+
let deployment_options = p.deployment_options();
|
|
90
|
+
return Some(SlotReservation {
|
|
91
|
+
slot,
|
|
92
|
+
deployment_options,
|
|
93
|
+
});
|
|
80
94
|
}
|
|
81
95
|
None
|
|
82
96
|
}
|
|
@@ -126,11 +140,12 @@ impl SlotManager {
|
|
|
126
140
|
}
|
|
127
141
|
|
|
128
142
|
/// Try to reserve a compatible processing slot in any of the registered workers.
|
|
143
|
+
/// Returns the slot and the worker's deployment options (if using deployment-based versioning).
|
|
129
144
|
pub(crate) fn try_reserve_wft_slot(
|
|
130
145
|
&self,
|
|
131
146
|
namespace: String,
|
|
132
147
|
task_queue: String,
|
|
133
|
-
) -> Option<
|
|
148
|
+
) -> Option<SlotReservation> {
|
|
134
149
|
self.manager
|
|
135
150
|
.read()
|
|
136
151
|
.try_reserve_wft_slot(namespace, task_queue)
|
|
@@ -188,6 +203,7 @@ mod tests {
|
|
|
188
203
|
});
|
|
189
204
|
mock_provider.expect_namespace().return_const(namespace);
|
|
190
205
|
mock_provider.expect_task_queue().return_const(task_queue);
|
|
206
|
+
mock_provider.expect_deployment_options().return_const(None);
|
|
191
207
|
mock_provider
|
|
192
208
|
}
|
|
193
209
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use crate::
|
|
1
|
+
use crate::WorkflowService;
|
|
2
2
|
use anyhow::{anyhow, bail};
|
|
3
3
|
use std::{fmt::Debug, marker::PhantomData};
|
|
4
4
|
use temporal_sdk_core_protos::{
|
|
@@ -11,6 +11,7 @@ use temporal_sdk_core_protos::{
|
|
|
11
11
|
workflowservice::v1::GetWorkflowExecutionHistoryRequest,
|
|
12
12
|
},
|
|
13
13
|
};
|
|
14
|
+
use tonic::IntoRequest;
|
|
14
15
|
|
|
15
16
|
/// Enumerates terminal states for a particular workflow execution
|
|
16
17
|
// TODO: Add non-proto failure types, flesh out details, etc.
|
|
@@ -81,7 +82,7 @@ impl WorkflowExecutionInfo {
|
|
|
81
82
|
/// Bind the workflow info to a specific client, turning it into a workflow handle
|
|
82
83
|
pub fn bind_untyped<CT>(self, client: CT) -> UntypedWorkflowHandle<CT>
|
|
83
84
|
where
|
|
84
|
-
CT:
|
|
85
|
+
CT: WorkflowService + Clone,
|
|
85
86
|
{
|
|
86
87
|
UntypedWorkflowHandle::new(client, self)
|
|
87
88
|
}
|
|
@@ -92,7 +93,7 @@ pub(crate) type UntypedWorkflowHandle<CT> = WorkflowHandle<CT, Vec<Payload>>;
|
|
|
92
93
|
|
|
93
94
|
impl<CT, RT> WorkflowHandle<CT, RT>
|
|
94
95
|
where
|
|
95
|
-
CT:
|
|
96
|
+
CT: WorkflowService + Clone,
|
|
96
97
|
// TODO: Make more generic, capable of (de)serialization w/ serde
|
|
97
98
|
RT: FromPayloadsExt,
|
|
98
99
|
{
|
|
@@ -125,18 +126,21 @@ where
|
|
|
125
126
|
let server_res = self
|
|
126
127
|
.client
|
|
127
128
|
.clone()
|
|
128
|
-
.get_workflow_execution_history(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
129
|
+
.get_workflow_execution_history(
|
|
130
|
+
GetWorkflowExecutionHistoryRequest {
|
|
131
|
+
namespace: self.info.namespace.to_string(),
|
|
132
|
+
execution: Some(WorkflowExecution {
|
|
133
|
+
workflow_id: self.info.workflow_id.clone(),
|
|
134
|
+
run_id: run_id.clone(),
|
|
135
|
+
}),
|
|
136
|
+
skip_archival: true,
|
|
137
|
+
wait_new_event: true,
|
|
138
|
+
history_event_filter_type: HistoryEventFilterType::CloseEvent as i32,
|
|
139
|
+
next_page_token: next_page_tok.clone(),
|
|
140
|
+
..Default::default()
|
|
141
|
+
}
|
|
142
|
+
.into_request(),
|
|
143
|
+
)
|
|
140
144
|
.await?
|
|
141
145
|
.into_inner();
|
|
142
146
|
|
|
@@ -200,8 +204,7 @@ where
|
|
|
200
204
|
o => Err(anyhow!(
|
|
201
205
|
"Server returned an event that didn't match the CloseEvent filter. \
|
|
202
206
|
This is either a server bug or a new event the SDK does not understand. \
|
|
203
|
-
Event details: {:?}"
|
|
204
|
-
o
|
|
207
|
+
Event details: {o:?}"
|
|
205
208
|
)),
|
|
206
209
|
};
|
|
207
210
|
}
|
package/sdk-core/core/Cargo.toml
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "temporal-sdk-core"
|
|
3
3
|
version = "0.1.0"
|
|
4
|
-
authors = [
|
|
4
|
+
authors = [
|
|
5
|
+
"Spencer Judge <spencer@temporal.io>",
|
|
6
|
+
"Vitaly Arbuzov <vitaly@temporal.io>",
|
|
7
|
+
]
|
|
5
8
|
edition = "2024"
|
|
6
9
|
license-file = { workspace = true }
|
|
7
10
|
description = "Library for building new Temporal SDKs"
|
|
@@ -14,64 +17,107 @@ categories = ["development-tools"]
|
|
|
14
17
|
|
|
15
18
|
[features]
|
|
16
19
|
default = ["otel", "prom"]
|
|
17
|
-
otel = [
|
|
18
|
-
"dep:
|
|
20
|
+
otel = [
|
|
21
|
+
"dep:opentelemetry",
|
|
22
|
+
"dep:opentelemetry_sdk",
|
|
23
|
+
"dep:opentelemetry-otlp",
|
|
24
|
+
"dep:hyper",
|
|
25
|
+
"dep:hyper-util",
|
|
26
|
+
"dep:http-body-util",
|
|
27
|
+
]
|
|
19
28
|
prom = ["dep:prometheus"]
|
|
20
29
|
tokio-console = ["console-subscriber"]
|
|
21
30
|
ephemeral-server = ["dep:flate2", "dep:reqwest", "dep:tar", "dep:zip"]
|
|
22
31
|
debug-plugin = ["dep:reqwest"]
|
|
32
|
+
test-utilities = ["dep:assert_matches", "dep:bimap"]
|
|
23
33
|
|
|
24
34
|
[dependencies]
|
|
25
35
|
anyhow = "1.0"
|
|
36
|
+
assert_matches = { version = "1.5", optional = true }
|
|
37
|
+
bimap = { version = "0.6.3", optional = true }
|
|
26
38
|
async-trait = "0.1"
|
|
27
39
|
console-subscriber = { version = "0.4", optional = true }
|
|
28
40
|
crossbeam-channel = "0.5"
|
|
29
41
|
crossbeam-queue = "0.3"
|
|
30
42
|
crossbeam-utils = "0.8"
|
|
31
|
-
dashmap = "6.
|
|
43
|
+
dashmap = "6.1"
|
|
32
44
|
derive_builder = { workspace = true }
|
|
33
45
|
derive_more = { workspace = true }
|
|
34
46
|
enum_dispatch = "0.3"
|
|
35
47
|
enum-iterator = "2"
|
|
36
|
-
flate2 = { version = "1.
|
|
48
|
+
flate2 = { version = "1.1", optional = true }
|
|
37
49
|
futures-util = { version = "0.3", default-features = false }
|
|
38
|
-
futures-channel = { version = "0.3", default-features = false, features = [
|
|
50
|
+
futures-channel = { version = "0.3", default-features = false, features = [
|
|
51
|
+
"std",
|
|
52
|
+
] }
|
|
39
53
|
gethostname = "1.0.2"
|
|
40
|
-
governor = "0.
|
|
54
|
+
governor = "0.10"
|
|
41
55
|
http-body-util = { version = "0.1", optional = true }
|
|
42
|
-
hyper = { version = "1.
|
|
43
|
-
hyper-util = { version = "0.1", features = [
|
|
56
|
+
hyper = { version = "1.7", optional = true }
|
|
57
|
+
hyper-util = { version = "0.1", features = [
|
|
58
|
+
"server",
|
|
59
|
+
"http1",
|
|
60
|
+
"http2",
|
|
61
|
+
"tokio",
|
|
62
|
+
], optional = true }
|
|
44
63
|
itertools = "0.14"
|
|
45
|
-
lru = "0.
|
|
64
|
+
lru = "0.16"
|
|
46
65
|
mockall = "0.13"
|
|
47
66
|
opentelemetry = { workspace = true, features = ["metrics"], optional = true }
|
|
48
|
-
opentelemetry_sdk = { version = "0.
|
|
49
|
-
|
|
67
|
+
opentelemetry_sdk = { version = "0.31", features = [
|
|
68
|
+
"rt-tokio",
|
|
69
|
+
"metrics",
|
|
70
|
+
"spec_unstable_metrics_views",
|
|
71
|
+
], optional = true }
|
|
72
|
+
opentelemetry-otlp = { version = "0.31", features = [
|
|
73
|
+
"tokio",
|
|
74
|
+
"metrics",
|
|
75
|
+
"tls",
|
|
76
|
+
"http-proto",
|
|
77
|
+
"grpc-tonic",
|
|
78
|
+
], optional = true }
|
|
50
79
|
parking_lot = { version = "0.12", features = ["send_guard"] }
|
|
51
80
|
pid = "4.0"
|
|
52
|
-
pin-project = "1.
|
|
81
|
+
pin-project = "1.1"
|
|
53
82
|
prometheus = { version = "0.14", optional = true }
|
|
54
83
|
prost = { workspace = true }
|
|
55
|
-
prost-types = {
|
|
84
|
+
prost-types = { workspace = true }
|
|
56
85
|
rand = "0.9"
|
|
57
|
-
reqwest = { version = "0.12", features = [
|
|
86
|
+
reqwest = { version = "0.12", features = [
|
|
87
|
+
"json",
|
|
88
|
+
"stream",
|
|
89
|
+
"rustls-tls-native-roots",
|
|
90
|
+
], default-features = false, optional = true }
|
|
58
91
|
ringbuf = "0.4"
|
|
59
92
|
serde = "1.0"
|
|
60
93
|
serde_json = "1.0"
|
|
61
94
|
siphasher = "1.0"
|
|
62
95
|
slotmap = "1.0"
|
|
63
|
-
sysinfo = { version = "0.
|
|
96
|
+
sysinfo = { version = "0.37", default-features = false, features = ["system"] }
|
|
64
97
|
tar = { version = "0.4", optional = true }
|
|
65
98
|
thiserror = { workspace = true }
|
|
66
|
-
tokio = { version = "1.
|
|
99
|
+
tokio = { version = "1.47", features = [
|
|
100
|
+
"rt",
|
|
101
|
+
"rt-multi-thread",
|
|
102
|
+
"parking_lot",
|
|
103
|
+
"time",
|
|
104
|
+
"fs",
|
|
105
|
+
"process",
|
|
106
|
+
] }
|
|
67
107
|
tokio-util = { version = "0.7", features = ["io", "io-util"] }
|
|
68
108
|
tokio-stream = "0.1"
|
|
69
109
|
tonic = { workspace = true, features = ["tls-ring", "tls-native-roots"] }
|
|
70
110
|
tracing = "0.1"
|
|
71
|
-
tracing-subscriber = { version = "0.3", default-features = false, features = [
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
111
|
+
tracing-subscriber = { version = "0.3", default-features = false, features = [
|
|
112
|
+
"parking_lot",
|
|
113
|
+
"env-filter",
|
|
114
|
+
"registry",
|
|
115
|
+
"ansi",
|
|
116
|
+
] }
|
|
117
|
+
url = "2.5"
|
|
118
|
+
uuid = { version = "1.18", features = ["v4"] }
|
|
119
|
+
# Only need specific features to decompress zip files for ephemeral server download
|
|
120
|
+
zip = { version = "4.6", optional = true, default-features = false, features = ["deflate", "bzip2", "zstd"] }
|
|
75
121
|
|
|
76
122
|
# 1st party local deps
|
|
77
123
|
[dependencies.temporal-sdk-core-api]
|
|
@@ -80,7 +126,7 @@ features = ["otel_impls"]
|
|
|
80
126
|
|
|
81
127
|
[dependencies.temporal-sdk-core-protos]
|
|
82
128
|
path = "../sdk-core-protos"
|
|
83
|
-
features = ["history_builders"]
|
|
129
|
+
features = ["history_builders", "test-utilities"]
|
|
84
130
|
|
|
85
131
|
[dependencies.temporal-client]
|
|
86
132
|
path = "../client"
|
|
@@ -89,14 +135,23 @@ path = "../client"
|
|
|
89
135
|
path = "../fsm"
|
|
90
136
|
|
|
91
137
|
[dev-dependencies]
|
|
92
|
-
assert_matches = "1.
|
|
93
|
-
bimap = "0.6.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
138
|
+
assert_matches = "1.5"
|
|
139
|
+
bimap = "0.6.3"
|
|
140
|
+
bytes = "1.10"
|
|
141
|
+
clap = { version = "4.5", features = ["derive"] }
|
|
142
|
+
criterion = { version = "0.7", features = ["async", "async_tokio"] }
|
|
143
|
+
rstest = "0.26"
|
|
144
|
+
semver = "1.0"
|
|
98
145
|
temporal-sdk = { path = "../sdk" }
|
|
99
|
-
tokio = { version = "1.
|
|
146
|
+
tokio = { version = "1.47", features = [
|
|
147
|
+
"rt",
|
|
148
|
+
"rt-multi-thread",
|
|
149
|
+
"parking_lot",
|
|
150
|
+
"time",
|
|
151
|
+
"fs",
|
|
152
|
+
"process",
|
|
153
|
+
"test-util",
|
|
154
|
+
] }
|
|
100
155
|
tokio-stream = { version = "0.1", features = ["net"] }
|
|
101
156
|
|
|
102
157
|
[[test]]
|
|
@@ -105,30 +160,39 @@ path = "../tests/main.rs"
|
|
|
105
160
|
# Prevents autodiscovery, and hence these getting run with `cargo test`. Run with
|
|
106
161
|
# `cargo test --test integ_tests`
|
|
107
162
|
test = false
|
|
108
|
-
required-features = [
|
|
163
|
+
required-features = [
|
|
164
|
+
"temporal-sdk-core-protos/serde_serialize",
|
|
165
|
+
"test-utilities",
|
|
166
|
+
"ephemeral-server",
|
|
167
|
+
]
|
|
109
168
|
|
|
110
169
|
[[test]]
|
|
111
170
|
name = "heavy_tests"
|
|
112
171
|
path = "../tests/heavy_tests.rs"
|
|
113
172
|
test = false
|
|
173
|
+
required-features = ["test-utilities"]
|
|
114
174
|
|
|
115
175
|
[[test]]
|
|
116
176
|
name = "manual_tests"
|
|
117
177
|
path = "../tests/manual_tests.rs"
|
|
118
178
|
test = false
|
|
179
|
+
required-features = ["test-utilities"]
|
|
119
180
|
|
|
120
181
|
[[test]]
|
|
121
182
|
name = "global_metric_tests"
|
|
122
183
|
path = "../tests/global_metric_tests.rs"
|
|
123
184
|
test = false
|
|
185
|
+
required-features = ["test-utilities"]
|
|
124
186
|
|
|
125
187
|
[[test]]
|
|
126
188
|
name = "cloud_tests"
|
|
127
189
|
path = "../tests/cloud_tests.rs"
|
|
128
190
|
test = false
|
|
191
|
+
required-features = ["test-utilities"]
|
|
129
192
|
|
|
130
193
|
[[bench]]
|
|
131
194
|
name = "workflow_replay"
|
|
195
|
+
path = "../tests/workflow_replay_bench.rs"
|
|
132
196
|
harness = false
|
|
133
197
|
|
|
134
198
|
# The integration test runner should compile with the same configuration as the
|
|
@@ -140,7 +204,12 @@ name = "integ_runner"
|
|
|
140
204
|
path = "../tests/runner.rs"
|
|
141
205
|
harness = false
|
|
142
206
|
test = false
|
|
143
|
-
required-features = [
|
|
207
|
+
required-features = [
|
|
208
|
+
"temporal-sdk-core-protos/serde_serialize",
|
|
209
|
+
"test-utilities",
|
|
210
|
+
"ephemeral-server",
|
|
211
|
+
]
|
|
212
|
+
|
|
144
213
|
|
|
145
214
|
[lints]
|
|
146
215
|
workspace = true
|