@temporalio/core-bridge 1.12.2 → 1.12.3
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/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 -1
- package/sdk-core/client/src/callback_based.rs +123 -0
- package/sdk-core/client/src/lib.rs +96 -28
- package/sdk-core/client/src/metrics.rs +33 -5
- package/sdk-core/client/src/raw.rs +40 -1
- package/sdk-core/client/src/retry.rs +12 -3
- package/sdk-core/core/src/lib.rs +4 -2
- package/sdk-core/core/src/pollers/poll_buffer.rs +62 -14
- package/sdk-core/core/src/worker/client.rs +9 -5
- package/sdk-core/core/src/worker/heartbeat.rs +3 -1
- package/sdk-core/core-api/src/worker.rs +2 -2
- package/sdk-core/core-c-bridge/Cargo.toml +2 -0
- package/sdk-core/core-c-bridge/include/temporal-sdk-core-c-bridge.h +105 -0
- package/sdk-core/core-c-bridge/src/client.rs +265 -8
- package/sdk-core/core-c-bridge/src/tests/context.rs +11 -0
- package/sdk-core/core-c-bridge/src/tests/mod.rs +179 -3
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/CODEOWNERS +1 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/README.md +1 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/VERSION +1 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/buf.yaml +1 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/request_response.proto +83 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/cloudservice/v1/service.proto +37 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/connectivityrule/v1/message.proto +64 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/identity/v1/message.proto +3 -1
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/namespace/v1/message.proto +10 -0
- package/sdk-core/sdk-core-protos/protos/api_cloud_upstream/temporal/api/cloud/operation/v1/message.proto +1 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv2.json +644 -9
- package/sdk-core/sdk-core-protos/protos/api_upstream/openapi/openapiv3.yaml +635 -21
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/batch/v1/message.proto +60 -2
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/common/v1/message.proto +84 -15
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/batch_operation.proto +3 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/enums/v1/task_queue.proto +11 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/history/v1/message.proto +5 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +1 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/sdk/v1/worker_config.proto +36 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/taskqueue/v1/message.proto +29 -0
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/worker/v1/message.proto +11 -1
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +122 -4
- package/sdk-core/sdk-core-protos/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +41 -0
- package/sdk-core/sdk-core-protos/src/lib.rs +5 -1
- package/sdk-core/test-utils/Cargo.toml +1 -0
- package/sdk-core/test-utils/src/lib.rs +90 -3
- package/sdk-core/tests/cloud_tests.rs +11 -74
- package/sdk-core/tests/integ_tests/client_tests.rs +14 -10
- package/sdk-core/tests/integ_tests/worker_tests.rs +8 -2
- package/sdk-core/tests/integ_tests/workflow_tests/activities.rs +13 -0
- package/sdk-core/tests/integ_tests/workflow_tests/priority.rs +2 -108
- package/sdk-core/tests/main.rs +3 -0
- package/sdk-core/tests/shared_tests/mod.rs +43 -0
- package/sdk-core/tests/shared_tests/priority.rs +155 -0
- package/src/client.rs +5 -0
|
@@ -121,7 +121,9 @@ impl WorkerHeartbeatData {
|
|
|
121
121
|
start_time: SystemTime::now(),
|
|
122
122
|
heartbeat_time: None,
|
|
123
123
|
worker_instance_key: Uuid::new_v4().to_string(),
|
|
124
|
-
heartbeat_interval: worker_config
|
|
124
|
+
heartbeat_interval: worker_config
|
|
125
|
+
.heartbeat_interval
|
|
126
|
+
.expect("WorkerHeartbeatData is only called when heartbeat_interval is Some"),
|
|
125
127
|
reset_notify,
|
|
126
128
|
}
|
|
127
129
|
}
|
|
@@ -165,8 +165,8 @@ pub struct WorkerConfig {
|
|
|
165
165
|
/// The interval within which the worker will send a heartbeat.
|
|
166
166
|
/// The timer is reset on each existing RPC call that also happens to send this data, like
|
|
167
167
|
/// `PollWorkflowTaskQueueRequest`.
|
|
168
|
-
#[builder(default
|
|
169
|
-
pub heartbeat_interval: Duration
|
|
168
|
+
#[builder(default)]
|
|
169
|
+
pub heartbeat_interval: Option<Duration>,
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
impl WorkerConfig {
|
|
@@ -10,6 +10,8 @@ crate-type = ["cdylib"]
|
|
|
10
10
|
[dependencies]
|
|
11
11
|
anyhow = "1.0"
|
|
12
12
|
async-trait = "0.1"
|
|
13
|
+
futures-util = { version = "0.3", default-features = false }
|
|
14
|
+
http = "1.1"
|
|
13
15
|
libc = "0.2"
|
|
14
16
|
prost = { workspace = true }
|
|
15
17
|
# We rely on Cargo semver rules not updating a 0.x to 0.y. Per the rand
|
|
@@ -58,6 +58,15 @@ typedef struct TemporalCoreCancellationToken TemporalCoreCancellationToken;
|
|
|
58
58
|
|
|
59
59
|
typedef struct TemporalCoreClient TemporalCoreClient;
|
|
60
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Representation of gRPC request for the callback.
|
|
63
|
+
*
|
|
64
|
+
* Note, temporal_core_client_grpc_override_request_respond is effectively the "free" call for
|
|
65
|
+
* each request. Each request _must_ call that and the request can no longer be valid after that
|
|
66
|
+
* call.
|
|
67
|
+
*/
|
|
68
|
+
typedef struct TemporalCoreClientGrpcOverrideRequest TemporalCoreClientGrpcOverrideRequest;
|
|
69
|
+
|
|
61
70
|
typedef struct TemporalCoreEphemeralServer TemporalCoreEphemeralServer;
|
|
62
71
|
|
|
63
72
|
typedef struct TemporalCoreForwardedLog TemporalCoreForwardedLog;
|
|
@@ -114,6 +123,20 @@ typedef struct TemporalCoreClientHttpConnectProxyOptions {
|
|
|
114
123
|
struct TemporalCoreByteArrayRef password;
|
|
115
124
|
} TemporalCoreClientHttpConnectProxyOptions;
|
|
116
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Callback that is invoked for every gRPC call if set on the client options.
|
|
128
|
+
*
|
|
129
|
+
* Note, temporal_core_client_grpc_override_request_respond is effectively the "free" call for
|
|
130
|
+
* each request. Each request _must_ call that and the request can no longer be valid after that
|
|
131
|
+
* call. However, all of that work and the respond call may be done well after this callback
|
|
132
|
+
* returns. No data lifetime is related to the callback invocation itself.
|
|
133
|
+
*
|
|
134
|
+
* Implementers should return as soon as possible and perform the network request in the
|
|
135
|
+
* background.
|
|
136
|
+
*/
|
|
137
|
+
typedef void (*TemporalCoreClientGrpcOverrideCallback)(struct TemporalCoreClientGrpcOverrideRequest *request,
|
|
138
|
+
void *user_data);
|
|
139
|
+
|
|
117
140
|
typedef struct TemporalCoreClientOptions {
|
|
118
141
|
struct TemporalCoreByteArrayRef target_url;
|
|
119
142
|
struct TemporalCoreByteArrayRef client_name;
|
|
@@ -125,6 +148,21 @@ typedef struct TemporalCoreClientOptions {
|
|
|
125
148
|
const struct TemporalCoreClientRetryOptions *retry_options;
|
|
126
149
|
const struct TemporalCoreClientKeepAliveOptions *keep_alive_options;
|
|
127
150
|
const struct TemporalCoreClientHttpConnectProxyOptions *http_connect_proxy_options;
|
|
151
|
+
/**
|
|
152
|
+
* If this is set, all gRPC calls go through it and no connection is made to server. The client
|
|
153
|
+
* connection call usually calls this for "GetSystemInfo" before the connect is complete. See
|
|
154
|
+
* the callback documentation for more important information about usage and data lifetimes.
|
|
155
|
+
*
|
|
156
|
+
* When a callback is set, target_url is not used to connect, but it must be set to a valid URL
|
|
157
|
+
* anyways in case it is used for logging or other reasons. Similarly, other connect-specific
|
|
158
|
+
* fields like tls_options, keep_alive_options, and http_connect_proxy_options will be
|
|
159
|
+
* completely ignored if a callback is set.
|
|
160
|
+
*/
|
|
161
|
+
TemporalCoreClientGrpcOverrideCallback grpc_override_callback;
|
|
162
|
+
/**
|
|
163
|
+
* Optional user data passed to each callback call.
|
|
164
|
+
*/
|
|
165
|
+
void *grpc_override_callback_user_data;
|
|
128
166
|
} TemporalCoreClientOptions;
|
|
129
167
|
|
|
130
168
|
typedef struct TemporalCoreByteArray {
|
|
@@ -147,6 +185,36 @@ typedef void (*TemporalCoreClientConnectCallback)(void *user_data,
|
|
|
147
185
|
struct TemporalCoreClient *success,
|
|
148
186
|
const struct TemporalCoreByteArray *fail);
|
|
149
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Response provided to temporal_core_client_grpc_override_request_respond. All values referenced
|
|
190
|
+
* inside here must live until that call returns.
|
|
191
|
+
*/
|
|
192
|
+
typedef struct TemporalCoreClientGrpcOverrideResponse {
|
|
193
|
+
/**
|
|
194
|
+
* Numeric gRPC status code, see https://grpc.io/docs/guides/status-codes/. 0 is success, non-0
|
|
195
|
+
* is failure.
|
|
196
|
+
*/
|
|
197
|
+
int32_t status_code;
|
|
198
|
+
/**
|
|
199
|
+
* Headers for the response if any. Note, this is meant for user-defined metadata/headers, and
|
|
200
|
+
* not the gRPC system headers (like :status or content-type).
|
|
201
|
+
*/
|
|
202
|
+
TemporalCoreMetadataRef headers;
|
|
203
|
+
/**
|
|
204
|
+
* Protobuf bytes for a successful response. Ignored if status_code is non-0.
|
|
205
|
+
*/
|
|
206
|
+
struct TemporalCoreByteArrayRef success_proto;
|
|
207
|
+
/**
|
|
208
|
+
* UTF-8 failure message. Ignored if status_code is 0.
|
|
209
|
+
*/
|
|
210
|
+
struct TemporalCoreByteArrayRef fail_message;
|
|
211
|
+
/**
|
|
212
|
+
* Optional details for the gRPC failure. If non-empty, this should be a protobuf-serialized
|
|
213
|
+
* google.rpc.Status. Ignored if status_code is 0.
|
|
214
|
+
*/
|
|
215
|
+
struct TemporalCoreByteArrayRef fail_details;
|
|
216
|
+
} TemporalCoreClientGrpcOverrideResponse;
|
|
217
|
+
|
|
150
218
|
typedef struct TemporalCoreRpcCallOptions {
|
|
151
219
|
enum TemporalCoreRpcService service;
|
|
152
220
|
struct TemporalCoreByteArrayRef rpc;
|
|
@@ -656,6 +724,43 @@ void temporal_core_client_update_metadata(struct TemporalCoreClient *client,
|
|
|
656
724
|
void temporal_core_client_update_api_key(struct TemporalCoreClient *client,
|
|
657
725
|
struct TemporalCoreByteArrayRef api_key);
|
|
658
726
|
|
|
727
|
+
/**
|
|
728
|
+
* Get a reference to the service name.
|
|
729
|
+
*
|
|
730
|
+
* Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
|
|
731
|
+
*/
|
|
732
|
+
struct TemporalCoreByteArrayRef temporal_core_client_grpc_override_request_service(const struct TemporalCoreClientGrpcOverrideRequest *req);
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Get a reference to the RPC name.
|
|
736
|
+
*
|
|
737
|
+
* Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
|
|
738
|
+
*/
|
|
739
|
+
struct TemporalCoreByteArrayRef temporal_core_client_grpc_override_request_rpc(const struct TemporalCoreClientGrpcOverrideRequest *req);
|
|
740
|
+
|
|
741
|
+
/**
|
|
742
|
+
* Get a reference to the service headers.
|
|
743
|
+
*
|
|
744
|
+
* Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
|
|
745
|
+
*/
|
|
746
|
+
TemporalCoreMetadataRef temporal_core_client_grpc_override_request_headers(const struct TemporalCoreClientGrpcOverrideRequest *req);
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Get a reference to the request protobuf bytes.
|
|
750
|
+
*
|
|
751
|
+
* Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
|
|
752
|
+
*/
|
|
753
|
+
struct TemporalCoreByteArrayRef temporal_core_client_grpc_override_request_proto(const struct TemporalCoreClientGrpcOverrideRequest *req);
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Complete the request, freeing all request data.
|
|
757
|
+
*
|
|
758
|
+
* The data referenced in the response must live until this function returns. Once this call is
|
|
759
|
+
* made, none of the request data should be considered valid.
|
|
760
|
+
*/
|
|
761
|
+
void temporal_core_client_grpc_override_request_respond(struct TemporalCoreClientGrpcOverrideRequest *req,
|
|
762
|
+
struct TemporalCoreClientGrpcOverrideResponse resp);
|
|
763
|
+
|
|
659
764
|
/**
|
|
660
765
|
* Client, options, and user data must live through callback.
|
|
661
766
|
*/
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
use crate::
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
use crate::MetadataRef;
|
|
5
|
-
use crate::UserDataHandle;
|
|
6
|
-
use crate::runtime::Runtime;
|
|
1
|
+
use crate::{
|
|
2
|
+
ByteArray, ByteArrayRef, CancellationToken, MetadataRef, UserDataHandle, runtime::Runtime,
|
|
3
|
+
};
|
|
7
4
|
|
|
5
|
+
use futures_util::FutureExt;
|
|
6
|
+
use prost::bytes::Bytes;
|
|
7
|
+
use std::cell::OnceCell;
|
|
8
8
|
use std::str::FromStr;
|
|
9
|
+
use std::sync::Arc;
|
|
10
|
+
use std::sync::atomic::AtomicBool;
|
|
11
|
+
use std::sync::atomic::Ordering;
|
|
9
12
|
use std::time::Duration;
|
|
10
13
|
use temporal_client::{
|
|
11
14
|
ClientKeepAliveConfig, ClientOptions as CoreClientOptions, ClientOptionsBuilder,
|
|
12
15
|
ClientTlsConfig, CloudService, ConfiguredClient, HealthService, HttpConnectProxyOptions,
|
|
13
16
|
OperatorService, RetryClient, RetryConfig, TemporalServiceClientWithMetrics, TestService,
|
|
14
|
-
TlsConfig, WorkflowService,
|
|
17
|
+
TlsConfig, WorkflowService, callback_based,
|
|
15
18
|
};
|
|
19
|
+
use tokio::sync::oneshot;
|
|
16
20
|
use tonic::metadata::MetadataKey;
|
|
17
21
|
use url::Url;
|
|
18
22
|
|
|
@@ -28,6 +32,17 @@ pub struct ClientOptions {
|
|
|
28
32
|
pub retry_options: *const ClientRetryOptions,
|
|
29
33
|
pub keep_alive_options: *const ClientKeepAliveOptions,
|
|
30
34
|
pub http_connect_proxy_options: *const ClientHttpConnectProxyOptions,
|
|
35
|
+
/// If this is set, all gRPC calls go through it and no connection is made to server. The client
|
|
36
|
+
/// connection call usually calls this for "GetSystemInfo" before the connect is complete. See
|
|
37
|
+
/// the callback documentation for more important information about usage and data lifetimes.
|
|
38
|
+
///
|
|
39
|
+
/// When a callback is set, target_url is not used to connect, but it must be set to a valid URL
|
|
40
|
+
/// anyways in case it is used for logging or other reasons. Similarly, other connect-specific
|
|
41
|
+
/// fields like tls_options, keep_alive_options, and http_connect_proxy_options will be
|
|
42
|
+
/// completely ignored if a callback is set.
|
|
43
|
+
pub grpc_override_callback: ClientGrpcOverrideCallback,
|
|
44
|
+
/// Optional user data passed to each callback call.
|
|
45
|
+
pub grpc_override_callback_user_data: *mut libc::c_void,
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
#[repr(C)]
|
|
@@ -106,12 +121,19 @@ pub extern "C" fn temporal_core_client_connect(
|
|
|
106
121
|
return;
|
|
107
122
|
}
|
|
108
123
|
};
|
|
124
|
+
// Create override if present
|
|
125
|
+
let service_override = options.grpc_override_callback.map(|cb| {
|
|
126
|
+
create_callback_based_grpc_service(runtime, cb, options.grpc_override_callback_user_data)
|
|
127
|
+
});
|
|
109
128
|
// Spawn async call
|
|
110
129
|
let user_data = UserDataHandle(user_data);
|
|
111
130
|
let core = runtime.core.clone();
|
|
112
131
|
runtime.core.tokio_handle().spawn(async move {
|
|
113
132
|
match core_options
|
|
114
|
-
.
|
|
133
|
+
.connect_no_namespace_with_service_override(
|
|
134
|
+
core.telemetry().get_temporal_metric_meter(),
|
|
135
|
+
service_override,
|
|
136
|
+
)
|
|
115
137
|
.await
|
|
116
138
|
{
|
|
117
139
|
Ok(core) => {
|
|
@@ -136,6 +158,75 @@ pub extern "C" fn temporal_core_client_connect(
|
|
|
136
158
|
});
|
|
137
159
|
}
|
|
138
160
|
|
|
161
|
+
fn create_callback_based_grpc_service(
|
|
162
|
+
runtime: &Runtime,
|
|
163
|
+
cb: unsafe extern "C" fn(request: *mut ClientGrpcOverrideRequest, user_data: *mut libc::c_void),
|
|
164
|
+
user_data: *mut libc::c_void,
|
|
165
|
+
) -> callback_based::CallbackBasedGrpcService {
|
|
166
|
+
let runtime = runtime.clone();
|
|
167
|
+
let user_data = Arc::new(UserDataHandle(user_data));
|
|
168
|
+
callback_based::CallbackBasedGrpcService {
|
|
169
|
+
callback: Arc::new(move |req| {
|
|
170
|
+
let runtime = runtime.clone();
|
|
171
|
+
let user_data = user_data.clone();
|
|
172
|
+
async move {
|
|
173
|
+
// Create a oneshot sender/receiver for the result
|
|
174
|
+
let (sender, receiver) = oneshot::channel();
|
|
175
|
+
|
|
176
|
+
// Create boxed request that is dropped when the caller sets the response. If the
|
|
177
|
+
// caller does not, this will be a memory leak.
|
|
178
|
+
//
|
|
179
|
+
// We have to cast this to a literal pointer integer because we use spawn_blocking
|
|
180
|
+
// and Rust can't validate things in either of two approaches. The first approach,
|
|
181
|
+
// just moving the *mut to spawn_blocking closure, will not work because it is not
|
|
182
|
+
// send (even if you wrap it in a marked-send struct). The second, approach, moving
|
|
183
|
+
// the box to the closure and into_raw'ing it there won't work because Rust thinks
|
|
184
|
+
// the "req" param to spawn_blocking may outlive this closure even though we're
|
|
185
|
+
// confident in our oneshot use this will never happen.
|
|
186
|
+
let req_ptr = Box::into_raw(Box::new(ClientGrpcOverrideRequest {
|
|
187
|
+
core: req,
|
|
188
|
+
built_headers: OnceCell::new(),
|
|
189
|
+
response_sender: sender,
|
|
190
|
+
})) as usize;
|
|
191
|
+
|
|
192
|
+
// We want to make sure it reached user code. If spawn_blocking fails _and_ it
|
|
193
|
+
// didn't reach user code, it is on us to drop the box.
|
|
194
|
+
let reached_user_code = Arc::new(AtomicBool::new(false));
|
|
195
|
+
|
|
196
|
+
// Spawn the callback as blocking, failing on join failure. We use spawn_blocking
|
|
197
|
+
// just in case the user is doing something blocking in their closure, but we ask
|
|
198
|
+
// them not to.
|
|
199
|
+
let reached_user_code_clone = reached_user_code.clone();
|
|
200
|
+
let spawn_ret = runtime
|
|
201
|
+
.core
|
|
202
|
+
.tokio_handle()
|
|
203
|
+
.spawn_blocking(move || unsafe {
|
|
204
|
+
reached_user_code_clone.store(true, Ordering::Relaxed);
|
|
205
|
+
cb(
|
|
206
|
+
req_ptr as *mut ClientGrpcOverrideRequest,
|
|
207
|
+
user_data.clone().0,
|
|
208
|
+
);
|
|
209
|
+
})
|
|
210
|
+
.await;
|
|
211
|
+
if let Err(err) = spawn_ret {
|
|
212
|
+
// Re-own box so it can be dropped if never reached user code
|
|
213
|
+
if !reached_user_code.load(Ordering::Relaxed) {
|
|
214
|
+
let _ = unsafe { Box::from_raw(req_ptr as *mut ClientGrpcOverrideRequest) };
|
|
215
|
+
}
|
|
216
|
+
return Err(tonic::Status::internal(format!("{err}")));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Wait result and return. The receiver failure in theory can never happen. If it
|
|
220
|
+
// does, it means somehow the sender was dropped, but our code ensures the sender
|
|
221
|
+
// is not dropped until a value is sent. That's why we're panicking here instead
|
|
222
|
+
// of turning this into a Tonic error.
|
|
223
|
+
receiver.await.expect("Unexpected receiver failure")
|
|
224
|
+
}
|
|
225
|
+
.boxed()
|
|
226
|
+
}),
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
139
230
|
#[unsafe(no_mangle)]
|
|
140
231
|
pub extern "C" fn temporal_core_client_free(client: *mut Client) {
|
|
141
232
|
unsafe {
|
|
@@ -164,6 +255,164 @@ pub extern "C" fn temporal_core_client_update_api_key(client: *mut Client, api_k
|
|
|
164
255
|
.set_api_key(api_key.to_option_string());
|
|
165
256
|
}
|
|
166
257
|
|
|
258
|
+
/// Callback that is invoked for every gRPC call if set on the client options.
|
|
259
|
+
///
|
|
260
|
+
/// Note, temporal_core_client_grpc_override_request_respond is effectively the "free" call for
|
|
261
|
+
/// each request. Each request _must_ call that and the request can no longer be valid after that
|
|
262
|
+
/// call. However, all of that work and the respond call may be done well after this callback
|
|
263
|
+
/// returns. No data lifetime is related to the callback invocation itself.
|
|
264
|
+
///
|
|
265
|
+
/// Implementers should return as soon as possible and perform the network request in the
|
|
266
|
+
/// background.
|
|
267
|
+
pub type ClientGrpcOverrideCallback = Option<
|
|
268
|
+
unsafe extern "C" fn(request: *mut ClientGrpcOverrideRequest, user_data: *mut libc::c_void),
|
|
269
|
+
>;
|
|
270
|
+
|
|
271
|
+
/// Representation of gRPC request for the callback.
|
|
272
|
+
///
|
|
273
|
+
/// Note, temporal_core_client_grpc_override_request_respond is effectively the "free" call for
|
|
274
|
+
/// each request. Each request _must_ call that and the request can no longer be valid after that
|
|
275
|
+
/// call.
|
|
276
|
+
pub struct ClientGrpcOverrideRequest {
|
|
277
|
+
core: callback_based::GrpcRequest,
|
|
278
|
+
built_headers: OnceCell<String>,
|
|
279
|
+
response_sender: oneshot::Sender<Result<callback_based::GrpcSuccessResponse, tonic::Status>>,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Expected to be passed to user thread
|
|
283
|
+
unsafe impl Send for ClientGrpcOverrideRequest {}
|
|
284
|
+
unsafe impl Sync for ClientGrpcOverrideRequest {}
|
|
285
|
+
|
|
286
|
+
/// Response provided to temporal_core_client_grpc_override_request_respond. All values referenced
|
|
287
|
+
/// inside here must live until that call returns.
|
|
288
|
+
#[repr(C)]
|
|
289
|
+
pub struct ClientGrpcOverrideResponse {
|
|
290
|
+
/// Numeric gRPC status code, see https://grpc.io/docs/guides/status-codes/. 0 is success, non-0
|
|
291
|
+
/// is failure.
|
|
292
|
+
pub status_code: i32,
|
|
293
|
+
|
|
294
|
+
/// Headers for the response if any. Note, this is meant for user-defined metadata/headers, and
|
|
295
|
+
/// not the gRPC system headers (like :status or content-type).
|
|
296
|
+
pub headers: MetadataRef,
|
|
297
|
+
|
|
298
|
+
/// Protobuf bytes for a successful response. Ignored if status_code is non-0.
|
|
299
|
+
pub success_proto: ByteArrayRef,
|
|
300
|
+
|
|
301
|
+
/// UTF-8 failure message. Ignored if status_code is 0.
|
|
302
|
+
pub fail_message: ByteArrayRef,
|
|
303
|
+
|
|
304
|
+
/// Optional details for the gRPC failure. If non-empty, this should be a protobuf-serialized
|
|
305
|
+
/// google.rpc.Status. Ignored if status_code is 0.
|
|
306
|
+
pub fail_details: ByteArrayRef,
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/// Get a reference to the service name.
|
|
310
|
+
///
|
|
311
|
+
/// Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
|
|
312
|
+
#[unsafe(no_mangle)]
|
|
313
|
+
pub extern "C" fn temporal_core_client_grpc_override_request_service(
|
|
314
|
+
req: *const ClientGrpcOverrideRequest,
|
|
315
|
+
) -> ByteArrayRef {
|
|
316
|
+
let req = unsafe { &*req };
|
|
317
|
+
req.core.service.as_str().into()
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/// Get a reference to the RPC name.
|
|
321
|
+
///
|
|
322
|
+
/// Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
|
|
323
|
+
#[unsafe(no_mangle)]
|
|
324
|
+
pub extern "C" fn temporal_core_client_grpc_override_request_rpc(
|
|
325
|
+
req: *const ClientGrpcOverrideRequest,
|
|
326
|
+
) -> ByteArrayRef {
|
|
327
|
+
let req = unsafe { &*req };
|
|
328
|
+
req.core.rpc.as_str().into()
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/// Get a reference to the service headers.
|
|
332
|
+
///
|
|
333
|
+
/// Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
|
|
334
|
+
#[unsafe(no_mangle)]
|
|
335
|
+
pub extern "C" fn temporal_core_client_grpc_override_request_headers(
|
|
336
|
+
req: *const ClientGrpcOverrideRequest,
|
|
337
|
+
) -> MetadataRef {
|
|
338
|
+
let req = unsafe { &*req };
|
|
339
|
+
// Lazily create the headers on first access
|
|
340
|
+
let headers = req.built_headers.get_or_init(|| {
|
|
341
|
+
req.core
|
|
342
|
+
.headers
|
|
343
|
+
.iter()
|
|
344
|
+
.filter_map(|(name, value)| value.to_str().ok().map(|val| (name.as_str(), val)))
|
|
345
|
+
.flat_map(|(k, v)| [k, v])
|
|
346
|
+
.collect::<Vec<_>>()
|
|
347
|
+
.join("\n")
|
|
348
|
+
});
|
|
349
|
+
headers.as_str().into()
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/// Get a reference to the request protobuf bytes.
|
|
353
|
+
///
|
|
354
|
+
/// Note, this is only valid until temporal_core_client_grpc_override_request_respond is called.
|
|
355
|
+
#[unsafe(no_mangle)]
|
|
356
|
+
pub extern "C" fn temporal_core_client_grpc_override_request_proto(
|
|
357
|
+
req: *const ClientGrpcOverrideRequest,
|
|
358
|
+
) -> ByteArrayRef {
|
|
359
|
+
let req = unsafe { &*req };
|
|
360
|
+
(&*req.core.proto).into()
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/// Complete the request, freeing all request data.
|
|
364
|
+
///
|
|
365
|
+
/// The data referenced in the response must live until this function returns. Once this call is
|
|
366
|
+
/// made, none of the request data should be considered valid.
|
|
367
|
+
#[unsafe(no_mangle)]
|
|
368
|
+
pub extern "C" fn temporal_core_client_grpc_override_request_respond(
|
|
369
|
+
req: *mut ClientGrpcOverrideRequest,
|
|
370
|
+
resp: ClientGrpcOverrideResponse,
|
|
371
|
+
) {
|
|
372
|
+
// This will be dropped at the end of this call
|
|
373
|
+
let req = unsafe { Box::from_raw(req) };
|
|
374
|
+
// Ignore failure if receiver no longer around (e.g. maybe a cancellation)
|
|
375
|
+
let _ = req
|
|
376
|
+
.response_sender
|
|
377
|
+
.send(resp.build_grpc_override_response());
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
impl ClientGrpcOverrideResponse {
|
|
381
|
+
#[allow(clippy::result_large_err)] // Tonic status, even though big, is reasonable as an Err
|
|
382
|
+
fn build_grpc_override_response(
|
|
383
|
+
self,
|
|
384
|
+
) -> Result<callback_based::GrpcSuccessResponse, tonic::Status> {
|
|
385
|
+
let headers = Self::client_headers_from_metadata_ref(self.headers)
|
|
386
|
+
.map_err(tonic::Status::internal)?;
|
|
387
|
+
if self.status_code == 0 {
|
|
388
|
+
Ok(callback_based::GrpcSuccessResponse {
|
|
389
|
+
headers,
|
|
390
|
+
proto: self.success_proto.to_vec(),
|
|
391
|
+
})
|
|
392
|
+
} else {
|
|
393
|
+
Err(tonic::Status::with_details_and_metadata(
|
|
394
|
+
tonic::Code::from_i32(self.status_code),
|
|
395
|
+
self.fail_message.to_string(),
|
|
396
|
+
Bytes::copy_from_slice(self.fail_details.to_slice()),
|
|
397
|
+
tonic::metadata::MetadataMap::from_headers(headers),
|
|
398
|
+
))
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
fn client_headers_from_metadata_ref(headers: MetadataRef) -> Result<http::HeaderMap, String> {
|
|
403
|
+
let key_values = headers.to_str_map_on_newlines();
|
|
404
|
+
let mut header_map = http::HeaderMap::with_capacity(key_values.len());
|
|
405
|
+
for (k, v) in key_values.into_iter() {
|
|
406
|
+
let name = http::HeaderName::try_from(k)
|
|
407
|
+
.map_err(|e| format!("Invalid header name '{k}': {e}"))?;
|
|
408
|
+
let value = http::HeaderValue::from_str(v)
|
|
409
|
+
.map_err(|e| format!("Invalid header value '{v}': {e}"))?;
|
|
410
|
+
header_map.insert(name, value);
|
|
411
|
+
}
|
|
412
|
+
Ok(header_map)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
167
416
|
#[repr(C)]
|
|
168
417
|
pub struct RpcCallOptions {
|
|
169
418
|
pub service: RpcService,
|
|
@@ -326,6 +575,7 @@ async fn call_workflow_service(
|
|
|
326
575
|
"DescribeWorkflowExecution" => rpc_call!(client, call, describe_workflow_execution),
|
|
327
576
|
"DescribeWorkflowRule" => rpc_call!(client, call, describe_workflow_rule),
|
|
328
577
|
"ExecuteMultiOperation" => rpc_call!(client, call, execute_multi_operation),
|
|
578
|
+
"FetchWorkerConfig" => rpc_call!(client, call, fetch_worker_config),
|
|
329
579
|
"GetClusterInfo" => rpc_call!(client, call, get_cluster_info),
|
|
330
580
|
"GetCurrentDeployment" => rpc_call!(client, call, get_current_deployment),
|
|
331
581
|
"GetDeploymentReachability" => rpc_call!(client, call, get_deployment_reachability),
|
|
@@ -419,6 +669,8 @@ async fn call_workflow_service(
|
|
|
419
669
|
}
|
|
420
670
|
"UpdateNamespace" => rpc_call_on_trait!(client, call, WorkflowService, update_namespace),
|
|
421
671
|
"UpdateSchedule" => rpc_call!(client, call, update_schedule),
|
|
672
|
+
"UpdateTaskQueueConfig" => rpc_call!(client, call, update_task_queue_config),
|
|
673
|
+
"UpdateWorkerConfig" => rpc_call!(client, call, update_worker_config),
|
|
422
674
|
"UpdateWorkerDeploymentVersionMetadata" => {
|
|
423
675
|
rpc_call!(client, call, update_worker_deployment_version_metadata)
|
|
424
676
|
}
|
|
@@ -525,6 +777,11 @@ async fn call_cloud_service(client: &CoreClient, call: &RpcCallOptions) -> anyho
|
|
|
525
777
|
"UpdateUserGroup" => rpc_call!(client, call, update_user_group),
|
|
526
778
|
"UpdateUser" => rpc_call!(client, call, update_user),
|
|
527
779
|
"ValidateNamespaceExportSink" => rpc_call!(client, call, validate_namespace_export_sink),
|
|
780
|
+
"UpdateNamespaceTags" => rpc_call!(client, call, update_namespace_tags),
|
|
781
|
+
"CreateConnectivityRule" => rpc_call!(client, call, create_connectivity_rule),
|
|
782
|
+
"GetConnectivityRule" => rpc_call!(client, call, get_connectivity_rule),
|
|
783
|
+
"GetConnectivityRules" => rpc_call!(client, call, get_connectivity_rules),
|
|
784
|
+
"DeleteConnectivityRule" => rpc_call!(client, call, delete_connectivity_rule),
|
|
528
785
|
rpc => Err(anyhow::anyhow!("Unknown RPC call {}", rpc)),
|
|
529
786
|
}
|
|
530
787
|
}
|
|
@@ -277,6 +277,15 @@ impl Context {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
pub fn client_connect(self: &Arc<Self>, options: Box<ClientOptions>) -> anyhow::Result<()> {
|
|
280
|
+
Self::client_connect_with_override(self, options, None, std::ptr::null_mut())
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
pub fn client_connect_with_override(
|
|
284
|
+
self: &Arc<Self>,
|
|
285
|
+
options: Box<ClientOptions>,
|
|
286
|
+
grpc_override_callback: crate::client::ClientGrpcOverrideCallback,
|
|
287
|
+
grpc_override_callback_user_data: *mut libc::c_void,
|
|
288
|
+
) -> anyhow::Result<()> {
|
|
280
289
|
let metadata = options
|
|
281
290
|
.headers
|
|
282
291
|
.as_ref()
|
|
@@ -339,6 +348,8 @@ impl Context {
|
|
|
339
348
|
retry_options: &*retry_options,
|
|
340
349
|
keep_alive_options: pointer_or_null(keep_alive_options.as_deref()),
|
|
341
350
|
http_connect_proxy_options: pointer_or_null(proxy_options.as_deref()),
|
|
351
|
+
grpc_override_callback,
|
|
352
|
+
grpc_override_callback_user_data,
|
|
342
353
|
});
|
|
343
354
|
|
|
344
355
|
let client_options_ptr = &*client_options as *const _;
|