@temporalio/core-bridge 1.12.0 → 1.12.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 +64 -119
- package/Cargo.toml +1 -1
- package/index.js +3 -2
- 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 +1 -2
- package/sdk-core/.github/workflows/per-pr.yml +2 -0
- package/sdk-core/AGENTS.md +7 -0
- package/sdk-core/Cargo.toml +9 -5
- package/sdk-core/README.md +6 -5
- package/sdk-core/client/Cargo.toml +3 -2
- package/sdk-core/client/src/lib.rs +17 -8
- package/sdk-core/client/src/metrics.rs +57 -23
- package/sdk-core/client/src/raw.rs +33 -15
- package/sdk-core/core/Cargo.toml +11 -9
- package/sdk-core/core/benches/workflow_replay.rs +114 -15
- package/sdk-core/core/src/core_tests/activity_tasks.rs +18 -18
- package/sdk-core/core/src/core_tests/child_workflows.rs +4 -4
- package/sdk-core/core/src/core_tests/determinism.rs +6 -6
- package/sdk-core/core/src/core_tests/local_activities.rs +20 -20
- package/sdk-core/core/src/core_tests/mod.rs +40 -5
- package/sdk-core/core/src/core_tests/queries.rs +25 -16
- package/sdk-core/core/src/core_tests/replay_flag.rs +3 -3
- package/sdk-core/core/src/core_tests/updates.rs +3 -3
- package/sdk-core/core/src/core_tests/workers.rs +9 -7
- package/sdk-core/core/src/core_tests/workflow_tasks.rs +40 -42
- package/sdk-core/core/src/ephemeral_server/mod.rs +1 -19
- package/sdk-core/core/src/lib.rs +10 -1
- package/sdk-core/core/src/pollers/poll_buffer.rs +2 -2
- package/sdk-core/core/src/replay/mod.rs +3 -3
- package/sdk-core/core/src/telemetry/metrics.rs +306 -152
- package/sdk-core/core/src/telemetry/mod.rs +11 -4
- package/sdk-core/core/src/telemetry/otel.rs +134 -131
- package/sdk-core/core/src/telemetry/prometheus_meter.rs +885 -0
- package/sdk-core/core/src/telemetry/prometheus_server.rs +48 -28
- package/sdk-core/core/src/test_help/mod.rs +27 -12
- package/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +7 -7
- package/sdk-core/core/src/worker/activities.rs +4 -4
- package/sdk-core/core/src/worker/client/mocks.rs +10 -3
- package/sdk-core/core/src/worker/client.rs +68 -5
- package/sdk-core/core/src/worker/heartbeat.rs +229 -0
- package/sdk-core/core/src/worker/mod.rs +35 -14
- package/sdk-core/core/src/worker/tuner/resource_based.rs +4 -4
- package/sdk-core/core/src/worker/workflow/history_update.rs +71 -19
- package/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +1 -2
- package/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +1 -1
- package/sdk-core/core/src/worker/workflow/machines/nexus_operation_state_machine.rs +31 -48
- package/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +1 -2
- package/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +3 -3
- package/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +4 -1
- package/sdk-core/core/src/worker/workflow/managed_run.rs +1 -1
- package/sdk-core/core/src/worker/workflow/mod.rs +15 -15
- package/sdk-core/core-api/Cargo.toml +2 -2
- package/sdk-core/core-api/src/envconfig.rs +204 -99
- package/sdk-core/core-api/src/lib.rs +9 -0
- package/sdk-core/core-api/src/telemetry/metrics.rs +548 -100
- package/sdk-core/core-api/src/worker.rs +11 -5
- package/sdk-core/core-c-bridge/Cargo.toml +49 -0
- package/sdk-core/core-c-bridge/build.rs +26 -0
- package/sdk-core/core-c-bridge/include/temporal-sdk-core-c-bridge.h +817 -0
- package/sdk-core/core-c-bridge/src/client.rs +679 -0
- package/sdk-core/core-c-bridge/src/lib.rs +245 -0
- package/sdk-core/core-c-bridge/src/metric.rs +682 -0
- package/sdk-core/core-c-bridge/src/random.rs +61 -0
- package/sdk-core/core-c-bridge/src/runtime.rs +445 -0
- package/sdk-core/core-c-bridge/src/testing.rs +282 -0
- package/sdk-core/core-c-bridge/src/tests/context.rs +644 -0
- package/sdk-core/core-c-bridge/src/tests/mod.rs +178 -0
- package/sdk-core/core-c-bridge/src/tests/utils.rs +108 -0
- package/sdk-core/core-c-bridge/src/worker.rs +1069 -0
- package/sdk-core/etc/deps.svg +64 -64
- package/sdk-core/sdk/src/activity_context.rs +6 -4
- package/sdk-core/sdk/src/lib.rs +49 -27
- package/sdk-core/sdk/src/workflow_future.rs +18 -25
- package/sdk-core/sdk-core-protos/protos/api_upstream/README.md +4 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/buf.yaml +0 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +630 -83
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +632 -78
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +4 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/command/v1/message.proto +6 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +2 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/deployment/v1/message.proto +32 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/common.proto +10 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/deployment.proto +26 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +2 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/reset.proto +4 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/failure/v1/message.proto +2 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +47 -31
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/nexus/v1/message.proto +4 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/schedule/v1/message.proto +7 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/worker/v1/message.proto +134 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflow/v1/message.proto +14 -11
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +148 -37
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +21 -0
- package/sdk-core/sdk-core-protos/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +4 -4
- package/sdk-core/sdk-core-protos/src/history_builder.rs +9 -5
- package/sdk-core/sdk-core-protos/src/lib.rs +96 -6
- package/sdk-core/test-utils/src/lib.rs +11 -3
- package/sdk-core/tests/cloud_tests.rs +3 -3
- package/sdk-core/tests/heavy_tests.rs +11 -3
- package/sdk-core/tests/integ_tests/client_tests.rs +12 -13
- package/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/metrics_tests.rs +188 -83
- package/sdk-core/tests/integ_tests/polling_tests.rs +1 -1
- package/sdk-core/tests/integ_tests/queries_tests.rs +56 -40
- package/sdk-core/tests/integ_tests/update_tests.rs +2 -7
- package/sdk-core/tests/integ_tests/worker_tests.rs +3 -4
- package/sdk-core/tests/integ_tests/worker_versioning_tests.rs +3 -7
- package/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +3 -5
- package/sdk-core/tests/integ_tests/workflow_tests/nexus.rs +24 -17
- package/src/client.rs +6 -0
- package/src/metrics.rs +6 -6
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
use crate::client::{
|
|
2
|
+
Client, ClientHttpConnectProxyOptions, ClientKeepAliveOptions, ClientRetryOptions,
|
|
3
|
+
ClientTlsOptions, RpcCallOptions, temporal_core_client_connect, temporal_core_client_free,
|
|
4
|
+
temporal_core_client_rpc_call,
|
|
5
|
+
};
|
|
6
|
+
use crate::runtime::{
|
|
7
|
+
Runtime, RuntimeOptions, RuntimeOrFail, temporal_core_byte_array_free,
|
|
8
|
+
temporal_core_runtime_free, temporal_core_runtime_new,
|
|
9
|
+
};
|
|
10
|
+
use crate::testing::{
|
|
11
|
+
DevServerOptions, EphemeralServer, TestServerOptions, temporal_core_ephemeral_server_free,
|
|
12
|
+
temporal_core_ephemeral_server_shutdown, temporal_core_ephemeral_server_start_dev_server,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
use crate::tests::utils::{
|
|
16
|
+
MetadataMap, OwnedRpcCallOptions, RpcCallError, byte_array_to_string, byte_array_to_vec,
|
|
17
|
+
pointer_or_null,
|
|
18
|
+
};
|
|
19
|
+
use crate::{ByteArray, ByteArrayRef};
|
|
20
|
+
use anyhow::anyhow;
|
|
21
|
+
use std::any::Any;
|
|
22
|
+
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
|
23
|
+
use std::ptr::NonNull;
|
|
24
|
+
use std::sync::{Arc, Condvar, Mutex, MutexGuard, PoisonError, Weak};
|
|
25
|
+
use std::time::Duration;
|
|
26
|
+
use temporal_client::ClientOptions;
|
|
27
|
+
use temporal_sdk_core::ephemeral_server::{
|
|
28
|
+
EphemeralExe, EphemeralExeVersion, TemporalDevServerConfig,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
#[derive(Debug)]
|
|
32
|
+
enum ContextOperationState {
|
|
33
|
+
Available,
|
|
34
|
+
InProgress,
|
|
35
|
+
CallbackOk(Option<Box<dyn Any + Send>>),
|
|
36
|
+
CallbackError(anyhow::Error),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
struct InnerContext {
|
|
40
|
+
is_disposed: bool,
|
|
41
|
+
operation_state: ContextOperationState,
|
|
42
|
+
runtime: *mut Runtime,
|
|
43
|
+
ephemeral_server: *mut EphemeralServer,
|
|
44
|
+
ephemeral_server_target: String,
|
|
45
|
+
client: *mut Client,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
unsafe impl Send for InnerContext {}
|
|
49
|
+
|
|
50
|
+
impl Default for InnerContext {
|
|
51
|
+
fn default() -> Self {
|
|
52
|
+
Self {
|
|
53
|
+
is_disposed: false,
|
|
54
|
+
operation_state: ContextOperationState::Available,
|
|
55
|
+
runtime: std::ptr::null_mut(),
|
|
56
|
+
ephemeral_server: std::ptr::null_mut(),
|
|
57
|
+
ephemeral_server_target: String::new(),
|
|
58
|
+
client: std::ptr::null_mut(),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
pub struct Context {
|
|
64
|
+
inner: Mutex<InnerContext>,
|
|
65
|
+
condvar: Condvar,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
impl Context {
|
|
69
|
+
fn new(inner: InnerContext) -> Arc<Self> {
|
|
70
|
+
Arc::new(Self {
|
|
71
|
+
inner: Mutex::new(inner),
|
|
72
|
+
condvar: Condvar::new(),
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn with(func: impl FnOnce(&Arc<Self>) + UnwindSafe) {
|
|
77
|
+
let context = Self::new(Default::default());
|
|
78
|
+
let func_panic = std::panic::catch_unwind(|| func(&context));
|
|
79
|
+
let dispose_panic = std::panic::catch_unwind(|| context.dispose());
|
|
80
|
+
if let Err(e) = func_panic.and(dispose_panic) {
|
|
81
|
+
std::panic::resume_unwind(e);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#[allow(dead_code)]
|
|
86
|
+
pub fn runtime(&self) -> anyhow::Result<Option<NonNull<Runtime>>> {
|
|
87
|
+
Ok(NonNull::new(self.wait_for_available()?.runtime))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
#[allow(dead_code)]
|
|
91
|
+
pub fn ephemeral_server(&self) -> anyhow::Result<Option<NonNull<EphemeralServer>>> {
|
|
92
|
+
Ok(NonNull::new(self.wait_for_available()?.ephemeral_server))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pub fn ephemeral_server_target(&self) -> anyhow::Result<Option<String>> {
|
|
96
|
+
let guard = self.wait_for_available()?;
|
|
97
|
+
Ok((!guard.ephemeral_server.is_null()).then(|| guard.ephemeral_server_target.clone()))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#[allow(dead_code)]
|
|
101
|
+
pub fn client(&self) -> anyhow::Result<Option<NonNull<Client>>> {
|
|
102
|
+
Ok(NonNull::new(self.wait_for_available()?.client))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
pub fn dispose(self: Arc<Context>) -> anyhow::Result<()> {
|
|
106
|
+
let inner = {
|
|
107
|
+
let mut guard = self
|
|
108
|
+
.inner
|
|
109
|
+
.lock()
|
|
110
|
+
.and_then(|lock| {
|
|
111
|
+
self.condvar.wait_while(lock, |inner| {
|
|
112
|
+
!matches!(inner.operation_state, ContextOperationState::Available)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
.unwrap_or_else(PoisonError::into_inner);
|
|
116
|
+
if guard.is_disposed {
|
|
117
|
+
return Err(anyhow!("Context already disposed"));
|
|
118
|
+
}
|
|
119
|
+
guard.is_disposed = true;
|
|
120
|
+
std::mem::take(&mut *guard)
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if !inner.client.is_null() {
|
|
124
|
+
temporal_core_client_free(inner.client);
|
|
125
|
+
}
|
|
126
|
+
if !inner.ephemeral_server.is_null() {
|
|
127
|
+
// Creating new non-disposed context to execute server shutdown in
|
|
128
|
+
let _ = Self::new(InnerContext {
|
|
129
|
+
runtime: inner.runtime,
|
|
130
|
+
ephemeral_server: inner.ephemeral_server,
|
|
131
|
+
..Default::default()
|
|
132
|
+
})
|
|
133
|
+
.ephemeral_server_shutdown();
|
|
134
|
+
}
|
|
135
|
+
if !inner.runtime.is_null() {
|
|
136
|
+
temporal_core_runtime_free(inner.runtime);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
Ok(())
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
pub fn runtime_new(self: &Arc<Self>) -> anyhow::Result<()> {
|
|
143
|
+
let mut guard = self.wait_for_available()?;
|
|
144
|
+
if !guard.runtime.is_null() {
|
|
145
|
+
return Err(anyhow!("Runtime already exists"));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
let RuntimeOrFail { runtime, fail } = temporal_core_runtime_new(&RuntimeOptions {
|
|
149
|
+
telemetry: std::ptr::null(),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if let Some(fail) = byte_array_to_string(runtime, fail) {
|
|
153
|
+
let runtime_is_null = if runtime.is_null() {
|
|
154
|
+
"(!!! runtime is null) "
|
|
155
|
+
} else {
|
|
156
|
+
temporal_core_runtime_free(runtime);
|
|
157
|
+
""
|
|
158
|
+
};
|
|
159
|
+
Err(anyhow!(
|
|
160
|
+
"Runtime creation failed: {}{}",
|
|
161
|
+
runtime_is_null,
|
|
162
|
+
fail
|
|
163
|
+
))
|
|
164
|
+
} else if runtime.is_null() {
|
|
165
|
+
Err(anyhow!("Runtime creation failed: runtime is null"))
|
|
166
|
+
} else {
|
|
167
|
+
guard.runtime = runtime;
|
|
168
|
+
Ok(())
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
pub fn start_dev_server(
|
|
173
|
+
self: &Arc<Self>,
|
|
174
|
+
config: Box<TemporalDevServerConfig>,
|
|
175
|
+
) -> anyhow::Result<()> {
|
|
176
|
+
let extra_args = config.extra_args.join("\n");
|
|
177
|
+
|
|
178
|
+
let mut test_server_options = Box::new(TestServerOptions {
|
|
179
|
+
existing_path: ByteArrayRef::empty(),
|
|
180
|
+
sdk_name: ByteArrayRef::empty(),
|
|
181
|
+
sdk_version: ByteArrayRef::empty(),
|
|
182
|
+
download_version: ByteArrayRef::empty(),
|
|
183
|
+
download_dest_dir: ByteArrayRef::empty(),
|
|
184
|
+
port: config.port.unwrap_or(0),
|
|
185
|
+
extra_args: extra_args.as_str().into(),
|
|
186
|
+
download_ttl_seconds: 0,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
match config.exe {
|
|
190
|
+
EphemeralExe::ExistingPath(ref path) => {
|
|
191
|
+
test_server_options.existing_path = path.as_str().into();
|
|
192
|
+
}
|
|
193
|
+
EphemeralExe::CachedDownload {
|
|
194
|
+
ref version,
|
|
195
|
+
ref dest_dir,
|
|
196
|
+
ref ttl,
|
|
197
|
+
} => {
|
|
198
|
+
test_server_options.download_dest_dir = dest_dir.as_deref().into();
|
|
199
|
+
test_server_options.download_ttl_seconds =
|
|
200
|
+
ttl.as_ref().map(Duration::as_secs).unwrap_or(0);
|
|
201
|
+
match version {
|
|
202
|
+
EphemeralExeVersion::SDKDefault {
|
|
203
|
+
sdk_name,
|
|
204
|
+
sdk_version,
|
|
205
|
+
} => {
|
|
206
|
+
test_server_options.download_version = "default".into();
|
|
207
|
+
test_server_options.sdk_name = sdk_name.as_str().into();
|
|
208
|
+
test_server_options.sdk_version = sdk_version.as_str().into();
|
|
209
|
+
}
|
|
210
|
+
EphemeralExeVersion::Fixed(version) => {
|
|
211
|
+
test_server_options.download_version = version.as_str().into();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let dev_server_options = Box::new(DevServerOptions {
|
|
218
|
+
test_server: &*test_server_options,
|
|
219
|
+
namespace: config.namespace.as_str().into(),
|
|
220
|
+
ip: config.ip.as_str().into(),
|
|
221
|
+
database_filename: config.db_filename.as_deref().into(),
|
|
222
|
+
ui: config.ui,
|
|
223
|
+
ui_port: config.ui_port.unwrap_or(0),
|
|
224
|
+
log_format: config.log.0.as_str().into(),
|
|
225
|
+
log_level: config.log.1.as_str().into(),
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
let dev_server_options_ptr = &*dev_server_options as *const _;
|
|
229
|
+
let user_data = Box::into_raw(Box::new(CallbackUserData {
|
|
230
|
+
data: (),
|
|
231
|
+
context: Arc::downgrade(self),
|
|
232
|
+
_allocations: Box::new((config, extra_args, test_server_options, dev_server_options)),
|
|
233
|
+
})) as *mut libc::c_void;
|
|
234
|
+
|
|
235
|
+
let runtime = {
|
|
236
|
+
let mut guard = self.wait_for_available()?;
|
|
237
|
+
if !guard.ephemeral_server.is_null() {
|
|
238
|
+
return Err(anyhow!("Ephemeral server already started"));
|
|
239
|
+
}
|
|
240
|
+
guard.operation_state = ContextOperationState::InProgress;
|
|
241
|
+
guard.runtime
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
temporal_core_ephemeral_server_start_dev_server(
|
|
245
|
+
runtime,
|
|
246
|
+
dev_server_options_ptr,
|
|
247
|
+
user_data,
|
|
248
|
+
ephemeral_server_start_callback,
|
|
249
|
+
);
|
|
250
|
+
self.wait_for_operation()
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
pub fn ephemeral_server_shutdown(self: &Arc<Context>) -> anyhow::Result<()> {
|
|
254
|
+
let server = {
|
|
255
|
+
let mut guard = self.wait_for_available()?;
|
|
256
|
+
let server = guard.ephemeral_server;
|
|
257
|
+
if server.is_null() {
|
|
258
|
+
return Err(anyhow!("Ephemeral server is null"));
|
|
259
|
+
}
|
|
260
|
+
guard.ephemeral_server = std::ptr::null_mut();
|
|
261
|
+
guard.ephemeral_server_target = String::new();
|
|
262
|
+
guard.operation_state = ContextOperationState::InProgress;
|
|
263
|
+
server
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
let user_data = Box::into_raw(Box::new(CallbackUserData {
|
|
267
|
+
data: server,
|
|
268
|
+
context: Arc::downgrade(self),
|
|
269
|
+
_allocations: Box::new(()),
|
|
270
|
+
})) as *mut libc::c_void;
|
|
271
|
+
temporal_core_ephemeral_server_shutdown(
|
|
272
|
+
server,
|
|
273
|
+
user_data,
|
|
274
|
+
ephemeral_server_shutdown_callback,
|
|
275
|
+
);
|
|
276
|
+
self.wait_for_operation()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
pub fn client_connect(self: &Arc<Self>, options: Box<ClientOptions>) -> anyhow::Result<()> {
|
|
280
|
+
let metadata = options
|
|
281
|
+
.headers
|
|
282
|
+
.as_ref()
|
|
283
|
+
.map(MetadataMap::serialize_from_map);
|
|
284
|
+
|
|
285
|
+
let tls_options = options.tls_cfg.as_ref().map(|tls_cfg| {
|
|
286
|
+
let client_tls_cfg = tls_cfg.client_tls_config.as_ref();
|
|
287
|
+
Box::new(ClientTlsOptions {
|
|
288
|
+
server_root_ca_cert: tls_cfg.server_root_ca_cert.as_deref().into(),
|
|
289
|
+
domain: tls_cfg.domain.as_deref().into(),
|
|
290
|
+
client_cert: client_tls_cfg.map(|c| c.client_cert.as_slice()).into(),
|
|
291
|
+
client_private_key: client_tls_cfg
|
|
292
|
+
.map(|c| c.client_private_key.as_slice())
|
|
293
|
+
.into(),
|
|
294
|
+
})
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
let retry_options = Box::new(ClientRetryOptions {
|
|
298
|
+
initial_interval_millis: options.retry_config.initial_interval.as_millis() as u64,
|
|
299
|
+
randomization_factor: options.retry_config.randomization_factor,
|
|
300
|
+
multiplier: options.retry_config.multiplier,
|
|
301
|
+
max_interval_millis: options.retry_config.max_interval.as_millis() as u64,
|
|
302
|
+
max_elapsed_time_millis: options
|
|
303
|
+
.retry_config
|
|
304
|
+
.max_elapsed_time
|
|
305
|
+
.as_ref()
|
|
306
|
+
.map(Duration::as_millis)
|
|
307
|
+
.unwrap_or(0) as u64,
|
|
308
|
+
max_retries: options.retry_config.max_retries,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
let keep_alive_options = options.keep_alive.as_ref().map(|keep_alive| {
|
|
312
|
+
Box::new(ClientKeepAliveOptions {
|
|
313
|
+
interval_millis: keep_alive.interval.as_millis() as u64,
|
|
314
|
+
timeout_millis: keep_alive.timeout.as_millis() as u64,
|
|
315
|
+
})
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
let proxy_options = options.http_connect_proxy.as_ref().map(|proxy| {
|
|
319
|
+
let (username, password) = match &proxy.basic_auth {
|
|
320
|
+
Some((username, password)) => (username.as_str().into(), password.as_str().into()),
|
|
321
|
+
None => (ByteArrayRef::empty(), ByteArrayRef::empty()),
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
Box::new(ClientHttpConnectProxyOptions {
|
|
325
|
+
target_host: proxy.target_addr.as_str().into(),
|
|
326
|
+
username,
|
|
327
|
+
password,
|
|
328
|
+
})
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
let client_options = Box::new(crate::client::ClientOptions {
|
|
332
|
+
target_url: options.target_url.as_str().into(),
|
|
333
|
+
client_name: options.client_name.as_str().into(),
|
|
334
|
+
client_version: options.client_version.as_str().into(),
|
|
335
|
+
metadata: metadata.as_deref().into(),
|
|
336
|
+
api_key: options.api_key.as_deref().into(),
|
|
337
|
+
identity: options.identity.as_str().into(),
|
|
338
|
+
tls_options: pointer_or_null(tls_options.as_deref()),
|
|
339
|
+
retry_options: &*retry_options,
|
|
340
|
+
keep_alive_options: pointer_or_null(keep_alive_options.as_deref()),
|
|
341
|
+
http_connect_proxy_options: pointer_or_null(proxy_options.as_deref()),
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
let client_options_ptr = &*client_options as *const _;
|
|
345
|
+
let user_data = Box::into_raw(Box::new(CallbackUserData {
|
|
346
|
+
data: (),
|
|
347
|
+
context: Arc::downgrade(self),
|
|
348
|
+
_allocations: Box::new((
|
|
349
|
+
options,
|
|
350
|
+
metadata,
|
|
351
|
+
tls_options,
|
|
352
|
+
retry_options,
|
|
353
|
+
keep_alive_options,
|
|
354
|
+
proxy_options,
|
|
355
|
+
client_options,
|
|
356
|
+
)),
|
|
357
|
+
})) as *mut libc::c_void;
|
|
358
|
+
|
|
359
|
+
let runtime = {
|
|
360
|
+
let mut guard = self.wait_for_available()?;
|
|
361
|
+
if !guard.client.is_null() {
|
|
362
|
+
return Err(anyhow!("Client already exists"));
|
|
363
|
+
}
|
|
364
|
+
guard.operation_state = ContextOperationState::InProgress;
|
|
365
|
+
guard.runtime
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
temporal_core_client_connect(
|
|
369
|
+
runtime,
|
|
370
|
+
client_options_ptr,
|
|
371
|
+
user_data,
|
|
372
|
+
client_connect_callback,
|
|
373
|
+
);
|
|
374
|
+
self.wait_for_operation()
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
pub fn rpc_call(
|
|
378
|
+
self: &Arc<Self>,
|
|
379
|
+
mut options: Box<OwnedRpcCallOptions>,
|
|
380
|
+
) -> anyhow::Result<Vec<u8>> {
|
|
381
|
+
let c_options = Box::new(RpcCallOptions {
|
|
382
|
+
service: options.service,
|
|
383
|
+
rpc: options.rpc.as_str().into(),
|
|
384
|
+
req: options.req.as_slice().into(),
|
|
385
|
+
retry: options.retry,
|
|
386
|
+
metadata: options.metadata.as_mut().map(MetadataMap::as_str).into(),
|
|
387
|
+
timeout_millis: options.timeout_millis,
|
|
388
|
+
cancellation_token: options
|
|
389
|
+
.cancellation_token
|
|
390
|
+
.unwrap_or_else(std::ptr::null_mut),
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
let (runtime, client) = {
|
|
394
|
+
let mut guard = self.wait_for_available()?;
|
|
395
|
+
let client = guard.client;
|
|
396
|
+
if client.is_null() {
|
|
397
|
+
return Err(anyhow!("Client is null"));
|
|
398
|
+
}
|
|
399
|
+
guard.operation_state = ContextOperationState::InProgress;
|
|
400
|
+
(guard.runtime, client)
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
let c_options_ptr = &*c_options as *const _;
|
|
404
|
+
let user_data = Box::into_raw(Box::new(CallbackUserData {
|
|
405
|
+
data: runtime,
|
|
406
|
+
context: Arc::downgrade(self),
|
|
407
|
+
_allocations: Box::new((options, c_options)),
|
|
408
|
+
})) as *mut libc::c_void;
|
|
409
|
+
|
|
410
|
+
temporal_core_client_rpc_call(client, c_options_ptr, user_data, rpc_call_callback);
|
|
411
|
+
self.wait_for_operation_result().map(|r| *r)
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
fn wait_while(
|
|
415
|
+
&self,
|
|
416
|
+
condition: impl FnMut(&mut InnerContext) -> bool,
|
|
417
|
+
) -> anyhow::Result<MutexGuard<'_, InnerContext>> {
|
|
418
|
+
self.inner
|
|
419
|
+
.lock()
|
|
420
|
+
.and_then(|lock| self.condvar.wait_while(lock, condition))
|
|
421
|
+
.map_err(|_| anyhow!("Context mutex poisoned"))
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
fn wait_for_available(&self) -> anyhow::Result<MutexGuard<'_, InnerContext>> {
|
|
425
|
+
let guard = self.wait_while(|inner| {
|
|
426
|
+
!matches!(inner.operation_state, ContextOperationState::Available) && !inner.is_disposed
|
|
427
|
+
})?;
|
|
428
|
+
|
|
429
|
+
if guard.is_disposed {
|
|
430
|
+
Err(anyhow!("Context already disposed"))
|
|
431
|
+
} else {
|
|
432
|
+
Ok(guard)
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/// First, runs provided function under context mutex lock. Then, asserts `operation_state` is
|
|
437
|
+
/// `InProgress` and updates it. Finally, notifies the context condvar. Panic inside `func` or wrong
|
|
438
|
+
/// `operation_state` will poison the mutex.
|
|
439
|
+
fn complete_operation_catch_unwind(
|
|
440
|
+
&self,
|
|
441
|
+
func: impl FnOnce(&mut MutexGuard<InnerContext>) -> ContextOperationState,
|
|
442
|
+
) -> std::thread::Result<()> {
|
|
443
|
+
let result = std::panic::catch_unwind(AssertUnwindSafe(|| {
|
|
444
|
+
let mut guard = self.inner.lock().unwrap();
|
|
445
|
+
let new_state = func(&mut guard);
|
|
446
|
+
assert!(matches!(
|
|
447
|
+
guard.operation_state,
|
|
448
|
+
ContextOperationState::InProgress
|
|
449
|
+
));
|
|
450
|
+
guard.operation_state = new_state;
|
|
451
|
+
}));
|
|
452
|
+
self.condvar.notify_all();
|
|
453
|
+
result
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/// Waits for operation state to become `CallbackOk` or `CallbackError`, then changes it to `Available`.
|
|
457
|
+
/// Panics on wrong operation state or if `CallbackOk` contains a result.
|
|
458
|
+
fn wait_for_operation(&self) -> anyhow::Result<()> {
|
|
459
|
+
self.wait_for_operation_any()
|
|
460
|
+
.map(|r| assert!(r.is_none(), "Expected empty callback result"))
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/// Waits for operation state to become `CallbackOk` or `CallbackError`, then changes it to `Available`.
|
|
464
|
+
/// Panics on wrong operation state or if `CallbackOk` has no result or the result is wrong type.
|
|
465
|
+
fn wait_for_operation_result<T>(&self) -> anyhow::Result<Box<T>>
|
|
466
|
+
where
|
|
467
|
+
T: Send + 'static,
|
|
468
|
+
{
|
|
469
|
+
self.wait_for_operation_any()
|
|
470
|
+
.map(|r| r.unwrap().downcast().unwrap())
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/// Waits for operation state to become `CallbackOk` or `CallbackError`, then changes it to `Available`.
|
|
474
|
+
/// Panics on wrong operation state.
|
|
475
|
+
fn wait_for_operation_any(&self) -> anyhow::Result<Option<Box<dyn Any + Send>>> {
|
|
476
|
+
let mut guard = self
|
|
477
|
+
.wait_while(|inner| matches!(inner.operation_state, ContextOperationState::InProgress))
|
|
478
|
+
.unwrap();
|
|
479
|
+
match std::mem::replace(&mut guard.operation_state, ContextOperationState::Available) {
|
|
480
|
+
ContextOperationState::CallbackOk(result) => Ok(result),
|
|
481
|
+
ContextOperationState::CallbackError(e) => Err(e),
|
|
482
|
+
other => panic!("Unexpected operation state {other:?}"),
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
struct CallbackUserData<T, C> {
|
|
488
|
+
data: T,
|
|
489
|
+
context: Weak<C>,
|
|
490
|
+
_allocations: Box<dyn Any + 'static>,
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
extern "C" fn ephemeral_server_start_callback(
|
|
494
|
+
user_data: *mut libc::c_void,
|
|
495
|
+
mut server: *mut EphemeralServer,
|
|
496
|
+
mut server_target: *const ByteArray,
|
|
497
|
+
mut fail: *const ByteArray,
|
|
498
|
+
) {
|
|
499
|
+
let user_data = unsafe { Box::from_raw(user_data as *mut CallbackUserData<(), Context>) };
|
|
500
|
+
if let Some(context) = user_data.context.upgrade() {
|
|
501
|
+
let _ = context.complete_operation_catch_unwind(|guard| {
|
|
502
|
+
let server_target =
|
|
503
|
+
byte_array_to_string(guard.runtime, std::mem::take(&mut server_target));
|
|
504
|
+
let fail = byte_array_to_string(guard.runtime, std::mem::take(&mut fail));
|
|
505
|
+
|
|
506
|
+
if let Some(fail) = fail {
|
|
507
|
+
ContextOperationState::CallbackError(anyhow!(
|
|
508
|
+
"Ephemeral server start failed: {}",
|
|
509
|
+
fail
|
|
510
|
+
))
|
|
511
|
+
} else if server.is_null() {
|
|
512
|
+
ContextOperationState::CallbackError(anyhow!(
|
|
513
|
+
"Ephemeral server start failed: server is null"
|
|
514
|
+
))
|
|
515
|
+
} else if let Some(server_target) = server_target {
|
|
516
|
+
guard.ephemeral_server = std::mem::take(&mut server);
|
|
517
|
+
guard.ephemeral_server_target = server_target;
|
|
518
|
+
ContextOperationState::CallbackOk(None)
|
|
519
|
+
} else {
|
|
520
|
+
ContextOperationState::CallbackError(anyhow!(
|
|
521
|
+
"Ephemeral server start failed: server target is null"
|
|
522
|
+
))
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if !server_target.is_null() {
|
|
528
|
+
temporal_core_byte_array_free(std::ptr::null_mut(), server_target);
|
|
529
|
+
}
|
|
530
|
+
if !fail.is_null() {
|
|
531
|
+
temporal_core_byte_array_free(std::ptr::null_mut(), fail);
|
|
532
|
+
}
|
|
533
|
+
if !server.is_null() {
|
|
534
|
+
// Creating new context as to not conflict with operation_status in original context
|
|
535
|
+
let shutdown_context = Context::new(InnerContext {
|
|
536
|
+
ephemeral_server: server,
|
|
537
|
+
..Default::default()
|
|
538
|
+
});
|
|
539
|
+
let _ = shutdown_context.ephemeral_server_shutdown();
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
extern "C" fn ephemeral_server_shutdown_callback(
|
|
544
|
+
user_data: *mut libc::c_void,
|
|
545
|
+
mut fail: *const ByteArray,
|
|
546
|
+
) {
|
|
547
|
+
let user_data = unsafe { Box::from_raw(user_data as *mut CallbackUserData<_, Context>) };
|
|
548
|
+
temporal_core_ephemeral_server_free(user_data.data);
|
|
549
|
+
|
|
550
|
+
if let Some(context) = user_data.context.upgrade() {
|
|
551
|
+
let _ = context.complete_operation_catch_unwind(|guard| {
|
|
552
|
+
if let Some(fail) = byte_array_to_string(guard.runtime, std::mem::take(&mut fail)) {
|
|
553
|
+
ContextOperationState::CallbackError(anyhow!(
|
|
554
|
+
"Ephemeral server shutdown failed: {}",
|
|
555
|
+
fail
|
|
556
|
+
))
|
|
557
|
+
} else {
|
|
558
|
+
ContextOperationState::CallbackOk(None)
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if !fail.is_null() {
|
|
564
|
+
temporal_core_byte_array_free(std::ptr::null_mut(), fail);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
extern "C" fn client_connect_callback(
|
|
569
|
+
user_data: *mut libc::c_void,
|
|
570
|
+
mut client: *mut Client,
|
|
571
|
+
mut fail: *const ByteArray,
|
|
572
|
+
) {
|
|
573
|
+
let user_data = unsafe { Box::from_raw(user_data as *mut CallbackUserData<(), Context>) };
|
|
574
|
+
if let Some(context) = user_data.context.upgrade() {
|
|
575
|
+
let _ = context.complete_operation_catch_unwind(|guard| {
|
|
576
|
+
if let Some(fail) = byte_array_to_string(guard.runtime, std::mem::take(&mut fail)) {
|
|
577
|
+
ContextOperationState::CallbackError(anyhow!("Client connect failed: {}", fail))
|
|
578
|
+
} else {
|
|
579
|
+
guard.client = std::mem::take(&mut client);
|
|
580
|
+
ContextOperationState::CallbackOk(None)
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if !fail.is_null() {
|
|
586
|
+
temporal_core_byte_array_free(std::ptr::null_mut(), fail);
|
|
587
|
+
}
|
|
588
|
+
if !client.is_null() {
|
|
589
|
+
temporal_core_client_free(client);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
extern "C" fn rpc_call_callback(
|
|
594
|
+
user_data: *mut libc::c_void,
|
|
595
|
+
mut success: *const ByteArray,
|
|
596
|
+
status_code: u32,
|
|
597
|
+
mut failure_message: *const ByteArray,
|
|
598
|
+
mut failure_details: *const ByteArray,
|
|
599
|
+
) {
|
|
600
|
+
let user_data = unsafe { Box::from_raw(user_data as *mut CallbackUserData<(), Context>) };
|
|
601
|
+
|
|
602
|
+
if let Some(context) = user_data.context.upgrade() {
|
|
603
|
+
let _ = context.complete_operation_catch_unwind(|guard| {
|
|
604
|
+
let success = byte_array_to_vec(guard.runtime, std::mem::take(&mut success));
|
|
605
|
+
let mut failure_message =
|
|
606
|
+
byte_array_to_string(guard.runtime, std::mem::take(&mut failure_message));
|
|
607
|
+
let failure_details =
|
|
608
|
+
byte_array_to_vec(guard.runtime, std::mem::take(&mut failure_details));
|
|
609
|
+
|
|
610
|
+
if failure_message.is_none() {
|
|
611
|
+
if status_code != 0 {
|
|
612
|
+
failure_message = Some("No message".into());
|
|
613
|
+
} else if failure_details.is_some() {
|
|
614
|
+
failure_message = Some("No message, failure details not empty".into());
|
|
615
|
+
} else if success.is_none() {
|
|
616
|
+
failure_message = Some("No message, response is null".into());
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if let Some(failure_message) = failure_message {
|
|
621
|
+
ContextOperationState::CallbackError(
|
|
622
|
+
RpcCallError {
|
|
623
|
+
status_code,
|
|
624
|
+
message: failure_message,
|
|
625
|
+
details: failure_details,
|
|
626
|
+
}
|
|
627
|
+
.into(),
|
|
628
|
+
)
|
|
629
|
+
} else {
|
|
630
|
+
ContextOperationState::CallbackOk(Some(Box::new(success.unwrap()) as _))
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if !success.is_null() {
|
|
636
|
+
temporal_core_byte_array_free(std::ptr::null_mut(), success);
|
|
637
|
+
}
|
|
638
|
+
if !failure_message.is_null() {
|
|
639
|
+
temporal_core_byte_array_free(std::ptr::null_mut(), failure_message);
|
|
640
|
+
}
|
|
641
|
+
if !failure_details.is_null() {
|
|
642
|
+
temporal_core_byte_array_free(std::ptr::null_mut(), failure_details);
|
|
643
|
+
}
|
|
644
|
+
}
|